January 11, 2020
This is an R Markdown Notebook version of ibrutinib_swath.R . In R Notebook, you can execute the code chunk by clicking the run button on the upper right corner of each chunk. The results will then appear beneath the code.
Information of the original code can be found below;
#######################################################################################
# ibrutinib_swath.R: A custom R script for automatic data preprocessing, analysis and #
# visualization of ibrutinib_swath dataset #
# Author: Somchai Chutipongtanate #
# Last update: April 22, 2019 #
# Source: https://github.com/schuti/ibrutinib_swath.R #
#######################################################################################
To use this script, please download and install R (version 3.4.4 or later) and RStudio (version 1.1.453 or later).
Once R and Rstuido installations finish, please open the file “R_Notebook_ibrutinib_swath.Rmd”. Since this script needs functions from several R packages, the first step is to install all package dependencies below. This step can be skipped if all required packages (as shown below) have already been installed.
# Install package dependencies
install.packages(c("readxl", "dplyr", "tidyr", "ggplot2", "ggrepel", "reshape2", "FactoMineR", "pheatmap"))
source("https://bioconductor.org/biocLite.R")
biocLite(c("biomaRt", "preprocessCore"))
Then, we load the required R packages.
# Load: R packages
library(readxl)
library(dplyr)
library(tidyr)
library(biomaRt) #
library(preprocessCore) #
library(ggplot2)
library(ggrepel)
library(reshape2)
library(FactoMineR) #
library(pheatmap)
The raw data (ibrutinib_SWATH.xlsx) is available via ProteomeXchange (PXD013402) and also downloadable as the supplementary dataset 1 once this dataset published. Please downlaod and place the file onto the desktop, so that it can be loaded into R.
# Load: ibrutinib-SWATH dataset (PXD013402)
setwd("~/Desktop")
data_path <- "~/Desktop/ibrutinib_SWATH.xlsx"
# Start: Data preprocess -----------------------------------------------------------------------------
## loading
group <- as.factor(c("WT", "WT", "WT", "WT+inh", "WT+inh", "WT+inh", "Q741x", "Q741x", "Q741x", "Q741x+inh", "Q741x+inh", "Q741x+inh"))
#group <- as.factor(c("W", "W", "W", "iW", "iW", "iW", "Q", "Q", "Q", "iQ", "iQ", "iQ"))
group <- factor(group, ordered = TRUE,
levels = c("Q741x+inh", "WT+inh", "Q741x", "WT"))
sample_label <- as.character(c("WT_1", "WT_2", "WT_3", "WT+inh_1", "WT+inh_2", "WT+inh_3", "Q741x_1", "Q741x_2", "Q741x_3", "Q741x+inh_1", "Q741x+inh_2", "Q741x+inh_3"))
#sample_label <- as.character(c("W1", "W2", "W3", "iW1", "iW2", "iW3", "Q1", "Q2", "Q3", "iQ1", "iQ2", "iQ3"))
areaPept <- read_excel(data_path, sheet = "Area - peptides")
areaProt <- read_excel(data_path, sheet = "Area - proteins")
Now the SWATH data at peptide and protein levels are ready for further analyses.
At the peptide level;
areaPept
At the protein level;
areaProt
In this analysis, we use SWATH quantitative data at the protein level for downstream data processing.
UniProt IDs can be mapped to gene names using useMart and getBM funcitons in BiomaRt package.
## gene mapping using biomaRt package (ref#1)
df <- areaProt[ ,1] %>%
tidyr::separate(Protein, c("sp", "uniProtID", "entry_name"), sep = "\\|") %>%
tidyr::separate(entry_name, c("entry_names", "species"), sep = "_") %>%
dplyr::select(uniProtID, entry_names, species)
ensembl <- useMart("ensembl", dataset="mmusculus_gene_ensembl",
host = "www.ensembl.org",
ensemblRedirect = FALSE)
tmp <- getBM(attributes = c('uniprotswissprot', 'external_gene_name'),
filters = 'uniprotswissprot',
values = df$uniProtID,
mart = ensembl)
Batch submitting query [============================================================>------------------------------] 67% eta: 1s
Batch submitting query [===========================================================================================] 100% eta: 0s
colnames(tmp) <- c('uniProtID', "gene.SYMBOL")
df <- left_join(df, tmp[!duplicated(tmp$uniProtID), ], by = "uniProtID")
ind <- is.na(df$gene.SYMBOL)
df$gene.SYMBOL[ind] <- df$entry_names[ind]
id_all <- df
id_all
For data preprocessing, the normalize.quantiles function of preprocessCore package is applied, while missing values are replaced by zero.
## Quantile normalization using preprocessCore package (ref#2)
expr_raw <- areaProt[ , 2:length(areaProt)]
colnames(expr_raw) <- sample_label
Quantile <- as.data.frame(normalize.quantiles(log2(as.matrix(expr_raw))))
colnames(Quantile) <- sample_label
## Missing values replaced by zero
ind <- which(is.na(Quantile), arr.ind = TRUE)
Quantile[ind] <- 0
expr_processed <- Quantile
## Collect datasets
raw_ds <- cbind(id_all, expr_raw)
process_ds <- cbind(id_all, expr_processed)
df <- t(expr_processed)
colnames(df) <- id_all$gene.SYMBOL
log_ds <- data.frame(group, df)
# End: Data preprocess -----------------------------------------------------------------------------
Once the preprocessing finished, we got the process dataset at the protein level, in which the quantitative data are expressed in log2 values.
process_ds
The data quality is checked by several measures. The first one is %coefficient of variation (CV).
# Start: Data analysis and visualization -----------------------------------------------------------------------------
## Group average
tmp <- data.frame(group = log_ds[ , 1], 2^log_ds[ , 2:length(log_ds)]) %>%
gather(gene.SYMBOL, expression, -group) %>%
dplyr::group_by(group, gene.SYMBOL) %>%
dplyr::summarize(group_mean = mean(expression)) %>%
spread(gene.SYMBOL, group_mean)
gr_avr <- as.data.frame(tmp[ , 2:length(tmp)])
rownames(gr_avr) <- tmp$group
gr_pair <- combn(unique(tmp$group), 2)
fc <- (gr_avr[gr_pair[1, ], ] / gr_avr[gr_pair[2, ], ]) %>% log2()
rownames(fc) <- paste0('log2', '(', gr_pair[1, ], '/', gr_pair[2, ], ')')
log2fc_ds <- fc
## Group SD
tmp <- data.frame(group = log_ds[ , 1], 2^log_ds[ , 2:length(log_ds)]) %>%
gather(gene.SYMBOL, expression, -group) %>%
dplyr::group_by(group, gene.SYMBOL) %>%
dplyr::summarize(group_sd = sd(expression)) %>%
spread(gene.SYMBOL, group_sd)
gr_sd <- as.data.frame(tmp[ , 2:length(tmp)])
rownames(gr_sd) <- tmp$group
## Coefficient of variation
qc <- 100 *gr_sd/gr_avr
qc <- data.frame(group = tmp$group, qc)
QC <- qc %>% gather(gene, CV, -group)
# Calculate median-CV of each group
medianCV <- QC %>% dplyr::group_by(group) %>% summarise(CV = round(median(CV), 1))
Median-CVs for each group;
print(paste0("Median-CV: Q741x+inh, ", medianCV[1,2], "%; WT+inh, ", medianCV[2,2], "%; Q741x, ", medianCV[3,2], "%; WT, ", medianCV[4,2], "%"))
[1] "Median-CV: Q741x+inh, 20.4%; WT+inh, 13%; Q741x, 17.2%; WT, 14.9%"
Violin plot of inter-group CV
# Violin plot of inter-group CV
plot.qc <- ggplot(QC, aes(x=group, y=CV)) +
geom_violin(aes(fill = as.character(group)), trim=FALSE, width = 0.8, #aes(fill = group),
na.rm = TRUE, position = "dodge")+
labs(fill = "") +
geom_boxplot(width=0.1, fill = 'white', outlier.size = 0,
na.rm = TRUE, position = "dodge")+
geom_text(data = medianCV, aes(label = CV), position = position_dodge(width = 1),
hjust = -0.5, vjust = -0.5, size = 5) +
xlab("") + ylab("% Coefficient of Variation") +
scale_y_continuous(breaks=c(0, 10, 20, 50, ceiling(max(QC$CV, na.rm=TRUE)))) +
theme_light(base_size = 12)
plot.qc

#pdf("QC_violinPlot.pdf", width = 6, height = 4)
#print(plot.qc)
#dev.off()
Here is the script;
## Violin plot of inter-group CV
#plot.qc <- ggplot(QC, aes(x=group, y=CV)) +
# geom_violin(aes(fill = as.character(group)), trim=FALSE, width = 0.8, #aes(fill = group),
# na.rm = TRUE, position = "dodge")+
# labs(fill = "") +
# geom_boxplot(width=0.1, fill = 'white', outlier.size = 0,
# na.rm = TRUE, position = "dodge")+
# geom_text(data = medianCV, aes(label = CV), position = position_dodge(width = 1),
# hjust = -0.5, vjust = -0.5, size = 5) +
# xlab("") + ylab("% Coefficient of Variation") +
# scale_y_continuous(breaks=c(0, 10, 20, 50, ceiling(max(QC$CV, na.rm=TRUE)))) +
# theme_light(base_size = 12)
#plot.qc
Correlation heatmap
## Correlation heatmap
corr <- 2^expr_processed
corr <- round(cor(corr, method = "pearson"),3)
corr[lower.tri(corr)] <- NA
melted_corr <- melt(corr, na.rm = TRUE)
plot_corrHM <- ggplot(data = melted_corr, aes(x = Var2, y = Var1, fill = value))+
geom_tile(color = "white")+
scale_fill_gradient2(low = "white", high = "red", mid = "yellow",
midpoint = 0.9, limit = c(0.8, 1), space = "Lab",
name= paste("Pearson", "\ncorrelation") ) +
labs(x = "", y = "") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, vjust = 1,
size = 8, hjust = 1)) +
coord_fixed() +
geom_text(aes(label = value), color = "black", size = 2) +
theme(axis.text.y = element_text(color = "black", size=8),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.ticks = element_blank(),
legend.justification = c(1, 0),
legend.position = c(0.6, 0.7),
legend.direction = "horizontal")+
guides(fill = guide_colorbar(barwidth = 7, barheight = 1,
title.position = "top", title.hjust = 0.5))
plot_corrHM

#pdf("plot_corrHM.pdf", width = 6, height = 4)
#print(plot_corrHM)
#dev.off()
Here is the script;
## Correlation heatmap
#corr <- 2^expr_processed
#corr <- round(cor(corr, method = "pearson"),3)
#corr[lower.tri(corr)] <- NA
#melted_corr <- melt(corr, na.rm = TRUE)
#plot_corrHM <- ggplot(data = melted_corr, aes(x = Var2, y = Var1, fill = value))+
# geom_tile(color = "white")+
# scale_fill_gradient2(low = "white", high = "red", mid = "yellow",
# midpoint = 0.9, limit = c(0.8, 1), space = "Lab",
# name= paste("Pearson", "\ncorrelation") ) +
# labs(x = "", y = "") +
# theme_minimal() +
# theme(axis.text.x = element_text(angle = 90, vjust = 1,
# size = 8, hjust = 1)) +
# coord_fixed() +
# geom_text(aes(label = value), color = "black", size = 2) +
# theme(axis.text.y = element_text(color = "black", size=8),
# panel.grid.major = element_blank(),
# panel.border = element_blank(),
# panel.background = element_blank(),
# axis.ticks = element_blank(),
# legend.justification = c(1, 0),
# legend.position = c(0.6, 0.7),
# legend.direction = "horizontal")+
# guides(fill = guide_colorbar(barwidth = 7, barheight = 1,
# title.position = "top", title.hjust = 0.5))
#plot_corrHM
The plot for the numbers of peptide per protein
## nPP plot
n_pept_prot <- areaPept %>%
dplyr::group_by(Protein) %>%
dplyr::summarize(n_pept = n()) %>%
arrange(desc(n_pept))
nPP <- data.frame(n_pept = c("1", "2-5", "6-10"),
n_prot = rbind(n_pept_prot %>% filter(n_pept ==1) %>% nrow(),
n_pept_prot %>% filter(n_pept >=2 & n_pept <= 5) %>% nrow(),
n_pept_prot %>% filter(n_pept >=6) %>% nrow()))
nPP_plot <- ggplot(nPP, aes(x = n_pept, y= n_prot)) +
geom_bar(stat = "identity", fill = "steelblue") +
ylim(0, max(nPP$n_prot)+50) +
geom_text(aes(label= n_prot), vjust=-0.3, color="black", size=4.5) +
geom_text(aes(label= paste0(round(100*n_prot/sum(n_prot), 1), "%")), vjust=1.6, color="white", size=4.5) +
xlab("Numbers of peptide") + ylab("Numbers of protein") +
theme_light(base_size = 12)
nPP_plot

#pdf("number_pept_prot.pdf", width = 4, height = 3)
#print(nPP_plot)
#dev.off()
Here is the script;
#n_pept_prot <- areaPept %>%
# dplyr::group_by(Protein) %>%
#dplyr::summarize(n_pept = n()) %>%
#arrange(desc(n_pept))
#nPP <- data.frame(n_pept = c("1", "2-5", "6-10"),
# n_prot = rbind(n_pept_prot %>% filter(n_pept ==1) %>% nrow(),
# n_pept_prot %>% filter(n_pept >=2 & n_pept <= 5) %>% nrow(),
# n_pept_prot %>% filter(n_pept >=6) %>% nrow()))
#nPP_plot <- ggplot(nPP, aes(x = n_pept, y= n_prot)) +
# geom_bar(stat = "identity", fill = "steelblue") +
# ylim(0, max(nPP$n_prot)+50) +
# geom_text(aes(label= n_prot), vjust=-0.3, color="black", size=4.5) +
# geom_text(aes(label= paste0(round(100*n_prot/sum(n_prot), 1), "%")), vjust=1.6, color="white", size=4.5) +
# xlab("Numbers of peptide") + ylab("Numbers of protein") +
# theme_light(base_size = 12)
#nPP_plot
PCA individual plot
## PCA individual plot using FactorMineR package (ref#3)
fit_pca <- PCA(log_ds[ , 2:length(log_ds)], graph = FALSE, scale.unit = TRUE)
percentage <- fit_pca$eig[ , 2]
PCs <- data.frame(fit_pca$ind$coord)
PCs$group <- as.character(group)
plotPCA <- ggplot(data = PCs, aes(x = Dim.1, y = Dim.2)) +
geom_point(aes(colour = group), size = 3) +
labs(colour = '') +
xlab(paste0('PC1', ' ', '(', round(percentage[1], 2), '%)')) +
ylab(paste0('PC2', ' ', '(', round(percentage[2], 2), '%)')) +
scale_fill_hue(l=40) +
coord_fixed(ratio=1, xlim=range(PCs$Dim.1), ylim=range(PCs$Dim.2)) +
geom_text_repel(label = rownames(PCs)) +
theme_light(base_size = 15)
plotPCA

#pdf("plotPCA.pdf", width = 6, height = 4)
#print(plotPCA)
#dev.off()
Here is the script;
## PCA individual plot using FactorMineR package (ref#3)
#fit_pca <- PCA(log_ds[ , 2:length(log_ds)], graph = FALSE, scale.unit = TRUE)
#percentage <- fit_pca$eig[ , 2]
#PCs <- data.frame(fit_pca$ind$coord)
#PCs$group <- as.character(group)
#plotPCA <- ggplot(data = PCs, aes(x = Dim.1, y = Dim.2)) +
# geom_point(aes(colour = group), size = 3) +
# labs(colour = '') +
# xlab(paste0('PC1', ' ', '(', round(percentage[1], 2), '%)')) +
# ylab(paste0('PC2', ' ', '(', round(percentage[2], 2), '%)')) +
# scale_fill_hue(l=40) +
# coord_fixed(ratio=1, xlim=range(PCs$Dim.1), ylim=range(PCs$Dim.2)) +
# geom_text_repel(label = rownames(PCs)) +
# theme_light(base_size = 15)
#plotPCA
Note that the contributions of protein variables of each component can be extracted from the fit_pca object for in-depth biological interpretation.
data.frame(fit_pca[["var"]][["contrib"]])
Lastly, the protein abundance heatmap (values in the log10 scale) where the missing values are mapped in black color.
## Protein abundance heatmap by pheatmap package (ref#4)
qc_hm <- 2^expr_processed
rownames(qc_hm) <- process_ds$gene.SYMBOL
for(i in seq_along(qc_hm)){
if(qc_hm[i] != 0){
qc_hm[i] <- log10(qc_hm[i])
} else {
qc_hm[i] <- 0
}}
n_missing <- sum(qc_hm == 0)
n_total <- dim(qc_hm)[1] * dim(qc_hm)[2]
print(paste0("QC_heatmap: Total ", n_total, " data points; ", n_missing, " missing values (", round(100*n_missing/n_total, 2), "%) showed in black)"))
[1] "QC_heatmap: Total 12564 data points; 7 missing values (0.06%) showed in black)"
qc_hm_plot <- pheatmap(t(qc_hm),
breaks = seq(0, max(qc_hm), length.out=101),
legend_breaks = seq(0, round(max(qc_hm), 0), length.out=8),
legend_labels = c("1e+00", "1e+01", "1e+02", "1e+03", "1e+04", "1e+05", "1e+06", "1e+07"),
color = colorRampPalette(c("black", "#8ea1ff", "#14ff57", "yellow", "orange", "#ea4444"))(100),
border_color = "gray",
clustering_distance_cols = "maximum",
clustering_method_columns = "complete",
cluster_rows = FALSE,
fontsize_row = 8, fontsize_col = 1,
scale = "none")

#main = paste0("Total ", n_total, " data points; ", n_missing, " missing values (", round(100*n_missing/n_total, 2), "%) showed in black)") )
#qc_hm_plot
#pdf("qc_heatmap.pdf", width = 8, height = 4)
#print(qc_hm_plot)
#dev.off()
Here is the script;
## Protein abundance heatmap by pheatmap package (ref#4)
#qc_hm <- 2^expr_processed
#rownames(qc_hm) <- process_ds$gene.SYMBOL
#for(i in seq_along(qc_hm)){
# if(qc_hm[i] != 0){
# qc_hm[i] <- log10(qc_hm[i])
# } else {
# qc_hm[i] <- 0
#}}
#n_missing <- sum(qc_hm == 0)
#n_total <- dim(qc_hm)[1] * dim(qc_hm)[2]
#qc_hm_plot <- pheatmap(t(qc_hm),
# breaks = seq(0, max(qc_hm), length.out=101),
# legend_breaks = seq(0, round(max(qc_hm), 0), length.out=8),
# legend_labels = c("1e+00", "1e+01", "1e+02", "1e+03", "1e+04", "1e+05", "1e+06", "1e+07"),
# color = colorRampPalette(c("black", "#8ea1ff", "#14ff57", "yellow", "orange", "#ea4444"))(100),
# border_color = "gray",
# clustering_distance_cols = "maximum",
# clustering_method_columns = "complete",
# cluster_rows = FALSE,
# fontsize_row = 8, fontsize_col = 1,
# scale = "none")
# #main = paste0("Total ", n_total, " data points; ", n_missing, " missing values (", round(100*n_missing/n_total, 2), "%) #showed in black)") )
#qc_hm_plot
Differntial expression analysis for multiple group comparison is performed by ANOVA with Tukey’s post-hoc.
## ANOVA with Tukey's post-hoc
tmp <- as.matrix(log_ds[, 2:length(log_ds)])
fit.aov <- aov(tmp ~ group)
output.aov <- summary.aov(fit.aov)
anova.pVal <- numeric(length = ncol(tmp))
for (i in 1:length(output.aov)){
anova.pVal[i] <- output.aov[[i]][1, 5]
}
adj.pVal <- matrix(nrow = ncol(tmp), ncol = nrow(log2fc_ds))
colnames(adj.pVal) <- paste(gr_pair[1, ], " vs ", gr_pair[2, ])
rownames(adj.pVal) <- colnames(tmp)
for (i in 1:ncol(tmp)){
adj.pVal[i, ] <- (TukeyHSD((aov(tmp[, i] ~ group))))[[1]][ ,4]
}
anova_ds <- cbind(anova.pVal, adj.pVal)
The ANOVA p-values (the first column) and the adjusted p-values from Tukey’s posthoc for each pair (labelled as the column name) are ready for further analyses.
data.frame(anova_ds)
Data including the fold changes and the adjusted p-values of proteins in each pairwise are ready for the volcano plots.
## Pairwise-Volcano plot
tmp <- data.frame(gene = rownames(anova_ds), anova_ds)
colnames(tmp) <- c("gene", "anova.pVal", paste0(gr_pair[1, ], "/", gr_pair[2, ]) )
long_ano <- gather(tmp, compare, adj_pVal, -gene, -anova.pVal)
fc.vp <- t(log2fc_ds)
fc.vp <- data.frame(gene = colnames(log2fc_ds), fc.vp)
colnames(fc.vp) <- c("gene", paste0(gr_pair[1, ], "/", gr_pair[2, ]) )
long_fc <- gather(fc.vp, compare, log2FC, -gene)
long_ano.fc <- long_ano %>%
left_join(long_fc, by = c("gene", "compare"))
long_ano.fc$gene <- as.character(long_ano.fc$gene)
Here is the mulitple pairwise volcano plots, where the red dots represent the relevant proteins based on the thresholds of >1.5x fold change and the adjusted p-value <0.05;
volcano_all <- ggplot(data = long_ano.fc, aes(x= log2FC, y=-log10(adj_pVal))) +
geom_point(aes(color = as.factor(abs(log2FC) >= log2(1.5) & anova.pVal < 0.05 & adj_pVal < 0.05)), size = 3, show.legend = FALSE) + #alpha = 0.5,
scale_color_manual(values = c("grey", "red")) +
xlab("log2 (fold change)") + ylab("-log10 (adjusted p-value)") +
# ggtitle(label = paste0("Volcano plot at ", 1.5,
# "x fold change and adjusted P-value < ", 0.05)) +
theme_grey(base_size = 15) +
geom_text_repel(data = (subset(long_ano.fc,
abs(log2FC) > 2 & -log10(adj_pVal) > 1.33 | -log10(adj_pVal) > 6)),
aes(label = gene, size = 0.1),
show.legend = FALSE,
colour = 'darkblue',
# box.padding = unit(0.35, "lines"),
point.padding = unit(0.5, "lines")
) +
facet_wrap(~ compare)
volcano_all

#print(paste0("Volcano_plot: thresholds at ", 1.5, "x fold change and adjusted P-value < ", 0.05))
#pdf("volcano_all.pdf", width = 12, height = 6)
#print(volcano_all)
#dev.off()
And the script;
#volcano_all <- ggplot(data = long_ano.fc, aes(x= log2FC, y=-log10(adj_pVal))) +
# geom_point(aes(color = as.factor(abs(log2FC) >= log2(1.5) & anova.pVal < 0.05 & adj_pVal < 0.05)), size = 3, show.legend = #FALSE) + #alpha = 0.5,
# scale_color_manual(values = c("grey", "red")) +
# xlab("log2 (fold change)") + ylab("-log10 (adjusted p-value)") +
# # ggtitle(label = paste0("Volcano plot at ", 1.5,
# # "x fold change and adjusted P-value < ", 0.05)) +
# theme_grey(base_size = 15) +
# geom_text_repel(data = (subset(long_ano.fc,
# abs(log2FC) > 2 & -log10(adj_pVal) > 1.33 | -log10(adj_pVal) > 6)),
# aes(label = gene, size = 0.1),
# show.legend = FALSE,
# colour = 'darkblue',
# # box.padding = unit(0.35, "lines"),
# point.padding = unit(0.5, "lines")
# ) +
# facet_wrap(~ compare)
#volcano_all
The lists of relevant proteins can be extracted from the long.ano.fc object;
For example, a list of 61 relevent proteins (in gene names) can be extracted from the Q741x+inh vs. Q741x comparision at the threshold of >1.5x fold changes and adj.pVal < 0.05. The protein list can be used for further biological interpretation;
long_ano.fc %>% filter(compare == "Q741x+inh/Q741x") %>% filter(abs(log2FC) >= log2(1.5)) %>% filter(adj_pVal < 0.05) %>% .$gene
[1] "Uba1" "Iqgap1" "Pabpc1" "Serpinb1a" "Rps4x" "Pgam1" "Mdh2" "Phb2" "Copb2" "Gsn" "Etfa"
[12] "Pfn1" "Capzb" "RL10A" "Psmd6" "Hsd17b4" "Uqcrc1" "Ssb" "Hnrnpab" "Rps27a" "Gcn1" "Rps15a"
[23] "Fdps" "Rps19" "Hars" "Sri" "Smarca5" "Sae1" "Dcps" "Tpp2" "Fth1" "Hnrnpul2" "Gm2000"
[34] "Rps25" "Dnpep" "Grb2" "Nme2" "Ahcyl1" "Lamp2" "Twf2" "Anp32b" "Cox6c" "Lpcat3" "Lnpep"
[45] "Metap2" "Rtn3" "Atp5d" "Ndufa12" "SNX3" "Krt2" "Tnfaip8" "Uqcr10" "Srp9" "VAMP8" "CPNS1"
[56] "Atp5k" "Nmt1" "Pdk3" "Rrm2" "Gabpa" "Sec61b"
Finally, the significant protein heatmap demonstrated several protein clusters distinct to each treatment conditions which can be used later for in-depth biological interpretation. The heatmap is plotted using the pheatmap function of pheatmap package.
## Significant protein heatmap by pheatmap (ref#4)
tmp <- as.matrix(log_ds[ , 2:length(log_ds)])
med <- apply(t(tmp), 1, mean)
medScale <- (t(tmp) - med)
tmp <- anova_ds[, 1]
medScale <- data.frame(medScale,
anova_pVal = tmp,
gene = rownames(medScale))
colnames(medScale) <- c("WT_1", "WT_2", "WT_3", "WT+inh_1", "WT+inh_2", "WT+inh_3", "Q741x_1", "Q741x_2", "Q741x_3", "Q741x+inh_1", "Q741x+inh_2", "Q741x+inh_3", "anova_pVal", "gene")
medScale_sig <- medScale %>% filter(anova_pVal < 0.05)
rownames(medScale_sig) <- medScale_sig$gene
medScale_sig <- medScale_sig[, 1: (length(medScale_sig) - 2)]
nprot_sig <- nrow(medScale_sig)
group <- factor(group, ordered = TRUE,
levels = c("WT+inh", "Q741x+inh", "Q741x", "WT"))
hm_sig <- pheatmap(medScale_sig, silent = FALSE,
breaks = seq(-(max(round(medScale_sig, 0))), max(round(medScale_sig, 0)), length.out=101),
legend_breaks = seq(-(max(round(medScale_sig, 0))), max(round(medScale_sig, 0)), length.out=5),
color = colorRampPalette(c("darkblue", "blue", "white", "orangered", "red"))(100),
border_color = NA,
annotation_col = data.frame(group = group, #factor(group),
row.names = sample_label),
clustering_distance_rows = "correlation",
clustering_distance_cols = "correlation",
clustering_method = "average",
fontsize_row = 2, fontsize_col = 10,
scale = "none")

#main = paste0(nprot_sig, " significant proteins (ANOVA p-value < ", 0.05, ")", "\nScale: Log2(fold change) with mean center", "\nClustering: Correlation distance and average linkage"))
#hm_sig
The heatmap parameters provided below are just for a reproducibility purpose.
print(paste0("Significant protein heatmap:", nprot_sig, " significant proteins (ANOVA p-value < ", 0.05, ")", "; Scale: Log2(fold change) with mean center", "; Clustering: Correlation distance and average linkage"))
[1] "Significant protein heatmap:397 significant proteins (ANOVA p-value < 0.05); Scale: Log2(fold change) with mean center; Clustering: Correlation distance and average linkage"
#pdf("heatmap_sig.pdf", width = 6, height = 9)
#print(hm_sig)
#dev.off()
This is the end of the script. Thank you.
# End: Data analysis and visualization -----------------------------------------------------------------------------
# References
## 1. Durinck S, Spellman P, Birney E, Huber W (2009). “Mapping identifiers for the integration of genomic datasets with the R/Bioconductor package biomaRt.” Nature Protocols, 4, 1184–1191.
## 2. Bolstad B (2018). preprocessCore: A collection of pre-processing functions. R package version 1.44.0,
## 3. Lê, S., Josse, J. & Husson, F. (2008). FactoMineR: An R Package for Multivariate Analysis. Journal of Statistical Software. 25(1). pp. 1-18.
## 4. Raivo Kolde (2018). pheatmap: Pretty Heatmaps. R package version 1.0.10.
Additional analysis#1: %coefficient of variation of peptide retention time (RT)
RT <- read_excel(data_path, sheet = "Observed RT")
RT <- RT %>% filter(Decoy == "FALSE")
RT <- RT[, c(2, 8:length(RT))]
colnames(RT) <- c("Peptides", sample_label)
tRT <- t(RT[, 2:length(RT)])
colnames(tRT) <- RT$Peptides
tRT <- data.frame(group, tRT)
## Group RT average
tmp_RT <- tRT %>%
gather(Peptides, RT, -group) %>%
dplyr::group_by(group, Peptides) %>%
dplyr::summarize(group_mean = mean(RT)) %>%
spread(Peptides, group_mean)
gr_RT_avr <- as.data.frame(tmp_RT[ , 2:length(tmp_RT)])
rownames(gr_RT_avr) <- tmp_RT$group
## Group RT SD
tmp_RT <- tRT %>%
gather(Peptides, RT, -group) %>%
dplyr::group_by(group, Peptides) %>%
dplyr::summarize(group_sd = sd(RT)) %>%
spread(Peptides, group_sd)
gr_RT_sd <- as.data.frame(tmp_RT[ , 2:length(tmp_RT)])
rownames(gr_RT_sd) <- tmp_RT$group
## Coefficient of variation
cv_RT <- 100 *gr_RT_sd/gr_RT_avr
cv_RT <- data.frame(group = tmp_RT$group, cv_RT)
cv_RT$group <- factor(cv_RT$group, ordered = TRUE,
levels = c("Q741x+inh", "WT+inh", "Q741x", "WT"))
CV_RT <- cv_RT %>% gather(Peptides, CV, -group)
# Calculate median-CV of each group
medianCV_RT <- CV_RT %>% dplyr::group_by(group) %>% summarise(CV = round(median(CV), 1))
print(paste0("Median-CV of peptide RT: Q741x+inh, ", medianCV_RT[1,2], "%; WT+inh, ", medianCV_RT[2,2], "%; Q741x, ", medianCV_RT[3,2], "%; WT, ", medianCV_RT[4,2], "%"))
[1] "Median-CV of peptide RT: Q741x+inh, 1.5%; WT+inh, 0.7%; Q741x, 1.5%; WT, 0.5%"
And here is the plot;
# Violin plot of inter-group CV
plot.cv_RT <- ggplot(CV_RT, aes(x=group, y=CV)) +
geom_violin(aes(fill = as.character(group)), trim=FALSE, width = 0.8, #aes(fill = group),
na.rm = TRUE, position = "dodge")+
geom_boxplot(width=0.1, fill = 'white', outlier.size = 0,
na.rm = TRUE, position = "dodge")+
#geom_boxplot(width=0.3, outlier.size = 0.1, na.rm = TRUE, position = "dodge", aes(fill = as.character(group)))+
geom_text(data = medianCV_RT, aes(label = CV), position = position_dodge(width = 1),
hjust = -0.5, vjust = -0.5, size = 5) +
ylim(0, 20)+
labs(fill = "")+
xlab("") + ylab("% Coefficient of Variation of peptide retention time") +
theme_light(base_size = 12)
plot.cv_RT

Additional analysis#2: Visualizing the overall shape of comparative data by a histogram of distribution of log2FC;
hist(long_ano.fc$log2FC, breaks = 120, col = "grey", xlab = "log2FC", main = "")

LS0tCnRpdGxlOiAnUiBOb3RlYm9vazogaWJydXRpbmliX1NXQVRIJwphdXRob3I6ICJTb21jaGFpIENodXRpcG9uZ3RhbmF0ZSIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCkphbnVhcnkgMTEsIDIwMjAKCgpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vayB2ZXJzaW9uIG9mIGlicnV0aW5pYl9zd2F0aC5SIC4gSW4gUiBOb3RlYm9vaywgeW91IGNhbiBleGVjdXRlIHRoZSBjb2RlIGNodW5rIGJ5IGNsaWNraW5nIHRoZSBydW4gYnV0dG9uIG9uIHRoZSB1cHBlciByaWdodCBjb3JuZXIgb2YgZWFjaCBjaHVuay4gVGhlIHJlc3VsdHMgd2lsbCB0aGVuIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLgoKSW5mb3JtYXRpb24gb2YgdGhlIG9yaWdpbmFsIGNvZGUgY2FuIGJlIGZvdW5kIGJlbG93OwpgYGB7cn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgaWJydXRpbmliX3N3YXRoLlI6IEEgY3VzdG9tIFIgc2NyaXB0IGZvciBhdXRvbWF0aWMgZGF0YSBwcmVwcm9jZXNzaW5nLCBhbmFseXNpcyBhbmQgIyAgICAgICAgICAgICAgICAgCiMgICAgICAgICAgICAgICAgICAgIHZpc3VhbGl6YXRpb24gb2YgaWJydXRpbmliX3N3YXRoIGRhdGFzZXQgICAgICAgICAgICAgICAgICAgICAgICAgIwojIEF1dGhvcjogU29tY2hhaSBDaHV0aXBvbmd0YW5hdGUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIExhc3QgdXBkYXRlOiBBcHJpbCAyMiwgMjAxOSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIFNvdXJjZTogaHR0cHM6Ly9naXRodWIuY29tL3NjaHV0aS9pYnJ1dGluaWJfc3dhdGguUiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKYGBgCgoKVG8gdXNlIHRoaXMgc2NyaXB0LCBwbGVhc2UgZG93bmxvYWQgYW5kIGluc3RhbGwgUiAodmVyc2lvbiAzLjQuNCBvciBsYXRlcikgYW5kIFJTdHVkaW8gKHZlcnNpb24gMS4xLjQ1MyBvciBsYXRlcikuICAKCk9uY2UgUiBhbmQgUnN0dWlkbyBpbnN0YWxsYXRpb25zIGZpbmlzaCwgcGxlYXNlIG9wZW4gdGhlIGZpbGUgIlJfTm90ZWJvb2tfaWJydXRpbmliX3N3YXRoLlJtZCIuIFNpbmNlIHRoaXMgc2NyaXB0IG5lZWRzIGZ1bmN0aW9ucyBmcm9tIHNldmVyYWwgUiBwYWNrYWdlcywgdGhlIGZpcnN0IHN0ZXAgaXMgdG8gaW5zdGFsbCBhbGwgcGFja2FnZSBkZXBlbmRlbmNpZXMgYmVsb3cuIFRoaXMgc3RlcCBjYW4gYmUgc2tpcHBlZCBpZiBhbGwgcmVxdWlyZWQgcGFja2FnZXMgKGFzIHNob3duIGJlbG93KSBoYXZlIGFscmVhZHkgYmVlbiBpbnN0YWxsZWQuCmBgYHtyfQojIEluc3RhbGwgcGFja2FnZSBkZXBlbmRlbmNpZXMKaW5zdGFsbC5wYWNrYWdlcyhjKCJyZWFkeGwiLCAiZHBseXIiLCAidGlkeXIiLCAiZ2dwbG90MiIsICJnZ3JlcGVsIiwgInJlc2hhcGUyIiwgIkZhY3RvTWluZVIiLCAicGhlYXRtYXAiKSkKc291cmNlKCJodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCmJpb2NMaXRlKGMoImJpb21hUnQiLCAicHJlcHJvY2Vzc0NvcmUiKSkKCmBgYAoKVGhlbiwgd2UgbG9hZCB0aGUgcmVxdWlyZWQgUiBwYWNrYWdlcy4KYGBge3J9CiMgTG9hZDogUiBwYWNrYWdlcwpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShiaW9tYVJ0KSAjCmxpYnJhcnkocHJlcHJvY2Vzc0NvcmUpICMKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoRmFjdG9NaW5lUikgIwpsaWJyYXJ5KHBoZWF0bWFwKQpgYGAKClRoZSByYXcgZGF0YSAoaWJydXRpbmliX1NXQVRILnhsc3gpIGlzIGF2YWlsYWJsZSB2aWEgUHJvdGVvbWVYY2hhbmdlIChQWEQwMTM0MDIpIGFuZCBhbHNvIGRvd25sb2FkYWJsZSBhcyB0aGUgc3VwcGxlbWVudGFyeSBkYXRhc2V0IDEgb25jZSB0aGlzIGRhdGFzZXQgcHVibGlzaGVkLiBQbGVhc2UgZG93bmxhb2QgYW5kIHBsYWNlIHRoZSBmaWxlIG9udG8gdGhlIGRlc2t0b3AsIHNvIHRoYXQgaXQgY2FuIGJlIGxvYWRlZCBpbnRvIFIuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTG9hZDogaWJydXRpbmliLVNXQVRIIGRhdGFzZXQgKFBYRDAxMzQwMikKc2V0d2QoIn4vRGVza3RvcCIpCmRhdGFfcGF0aCA8LSAifi9EZXNrdG9wL2licnV0aW5pYl9TV0FUSC54bHN4IiAKCiMgU3RhcnQ6IERhdGEgcHJlcHJvY2VzcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyBsb2FkaW5nCmdyb3VwIDwtIGFzLmZhY3RvcihjKCJXVCIsICJXVCIsICJXVCIsICJXVCtpbmgiLCAiV1QraW5oIiwgIldUK2luaCIsICJRNzQxeCIsICJRNzQxeCIsICJRNzQxeCIsICJRNzQxeCtpbmgiLCAiUTc0MXgraW5oIiwgIlE3NDF4K2luaCIpKQojZ3JvdXAgPC0gYXMuZmFjdG9yKGMoIlciLCAiVyIsICJXIiwgImlXIiwgImlXIiwgImlXIiwgIlEiLCAiUSIsICJRIiwgImlRIiwgImlRIiwgImlRIikpCmdyb3VwIDwtIGZhY3Rvcihncm91cCwgb3JkZXJlZCA9IFRSVUUsIAogICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiUTc0MXgraW5oIiwgIldUK2luaCIsICJRNzQxeCIsICJXVCIpKQoKc2FtcGxlX2xhYmVsIDwtIGFzLmNoYXJhY3RlcihjKCJXVF8xIiwgIldUXzIiLCAiV1RfMyIsICJXVCtpbmhfMSIsICJXVCtpbmhfMiIsICJXVCtpbmhfMyIsICJRNzQxeF8xIiwgIlE3NDF4XzIiLCAiUTc0MXhfMyIsICJRNzQxeCtpbmhfMSIsICJRNzQxeCtpbmhfMiIsICJRNzQxeCtpbmhfMyIpKQojc2FtcGxlX2xhYmVsIDwtIGFzLmNoYXJhY3RlcihjKCJXMSIsICJXMiIsICJXMyIsICJpVzEiLCAiaVcyIiwgImlXMyIsICJRMSIsICJRMiIsICJRMyIsICJpUTEiLCAiaVEyIiwgImlRMyIpKQphcmVhUGVwdCA8LSByZWFkX2V4Y2VsKGRhdGFfcGF0aCwgc2hlZXQgPSAiQXJlYSAtIHBlcHRpZGVzIikKYXJlYVByb3QgPC0gcmVhZF9leGNlbChkYXRhX3BhdGgsIHNoZWV0ID0gIkFyZWEgLSBwcm90ZWlucyIpCgpgYGAKCgpOb3cgdGhlIFNXQVRIIGRhdGEgYXQgcGVwdGlkZSBhbmQgcHJvdGVpbiBsZXZlbHMgYXJlIHJlYWR5IGZvciBmdXJ0aGVyIGFuYWx5c2VzLgoKQXQgdGhlIHBlcHRpZGUgbGV2ZWw7CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmFyZWFQZXB0CmBgYAoKQXQgdGhlIHByb3RlaW4gbGV2ZWw7CmBgYHtyfQphcmVhUHJvdApgYGAKSW4gdGhpcyBhbmFseXNpcywgd2UgdXNlIFNXQVRIIHF1YW50aXRhdGl2ZSBkYXRhIGF0IHRoZSBwcm90ZWluIGxldmVsIGZvciBkb3duc3RyZWFtIGRhdGEgcHJvY2Vzc2luZy4gIAoKClVuaVByb3QgSURzIGNhbiBiZSBtYXBwZWQgdG8gZ2VuZSBuYW1lcyB1c2luZyB1c2VNYXJ0IGFuZCBnZXRCTSBmdW5jaXRvbnMgaW4gQmlvbWFSdCBwYWNrYWdlLgpgYGB7cn0KIyMgZ2VuZSBtYXBwaW5nIHVzaW5nIGJpb21hUnQgcGFja2FnZSAocmVmIzEpCmRmIDwtIGFyZWFQcm90WyAsMV0gJT4lIAogIHRpZHlyOjpzZXBhcmF0ZShQcm90ZWluLCBjKCJzcCIsICJ1bmlQcm90SUQiLCAiZW50cnlfbmFtZSIpLCBzZXAgPSAiXFx8IikgJT4lCiAgdGlkeXI6OnNlcGFyYXRlKGVudHJ5X25hbWUsIGMoImVudHJ5X25hbWVzIiwgInNwZWNpZXMiKSwgc2VwID0gIl8iKSAlPiUKICBkcGx5cjo6c2VsZWN0KHVuaVByb3RJRCwgZW50cnlfbmFtZXMsIHNwZWNpZXMpIAplbnNlbWJsIDwtIHVzZU1hcnQoImVuc2VtYmwiLCBkYXRhc2V0PSJtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsIiwKICAgICAgICAgICAgICAgICAgIGhvc3QgPSAid3d3LmVuc2VtYmwub3JnIiwKICAgICAgICAgICAgICAgICAgIGVuc2VtYmxSZWRpcmVjdCA9IEZBTFNFKQp0bXAgPC0gZ2V0Qk0oYXR0cmlidXRlcyA9IGMoJ3VuaXByb3Rzd2lzc3Byb3QnLCAnZXh0ZXJuYWxfZ2VuZV9uYW1lJyksIAogICAgICAgICAgICAgZmlsdGVycyA9ICd1bmlwcm90c3dpc3Nwcm90JywgCiAgICAgICAgICAgICB2YWx1ZXMgPSBkZiR1bmlQcm90SUQsIAogICAgICAgICAgICAgbWFydCA9IGVuc2VtYmwpIApjb2xuYW1lcyh0bXApIDwtIGMoJ3VuaVByb3RJRCcsICJnZW5lLlNZTUJPTCIpCmRmIDwtIGxlZnRfam9pbihkZiwgdG1wWyFkdXBsaWNhdGVkKHRtcCR1bmlQcm90SUQpLCBdLCBieSA9ICJ1bmlQcm90SUQiKSAKaW5kIDwtIGlzLm5hKGRmJGdlbmUuU1lNQk9MKQpkZiRnZW5lLlNZTUJPTFtpbmRdIDwtIGRmJGVudHJ5X25hbWVzW2luZF0KaWRfYWxsIDwtIGRmCmBgYApgYGB7cn0KaWRfYWxsCmBgYAoKRm9yIGRhdGEgcHJlcHJvY2Vzc2luZywgdGhlIG5vcm1hbGl6ZS5xdWFudGlsZXMgZnVuY3Rpb24gb2YgcHJlcHJvY2Vzc0NvcmUgcGFja2FnZSBpcyBhcHBsaWVkLCB3aGlsZSBtaXNzaW5nIHZhbHVlcyBhcmUgcmVwbGFjZWQgYnkgemVyby4KYGBge3J9CiMjIFF1YW50aWxlIG5vcm1hbGl6YXRpb24gdXNpbmcgcHJlcHJvY2Vzc0NvcmUgcGFja2FnZSAocmVmIzIpCmV4cHJfcmF3IDwtIGFyZWFQcm90WyAsIDI6bGVuZ3RoKGFyZWFQcm90KV0KY29sbmFtZXMoZXhwcl9yYXcpIDwtIHNhbXBsZV9sYWJlbApRdWFudGlsZSA8LSBhcy5kYXRhLmZyYW1lKG5vcm1hbGl6ZS5xdWFudGlsZXMobG9nMihhcy5tYXRyaXgoZXhwcl9yYXcpKSkpCmNvbG5hbWVzKFF1YW50aWxlKSA8LSBzYW1wbGVfbGFiZWwKCiMjIE1pc3NpbmcgdmFsdWVzIHJlcGxhY2VkIGJ5IHplcm8KaW5kIDwtIHdoaWNoKGlzLm5hKFF1YW50aWxlKSwgYXJyLmluZCA9IFRSVUUpClF1YW50aWxlW2luZF0gPC0gMApleHByX3Byb2Nlc3NlZCA8LSBRdWFudGlsZQoKIyMgQ29sbGVjdCBkYXRhc2V0cwpyYXdfZHMgPC0gY2JpbmQoaWRfYWxsLCBleHByX3JhdykKcHJvY2Vzc19kcyA8LSBjYmluZChpZF9hbGwsIGV4cHJfcHJvY2Vzc2VkKSAKZGYgPC0gdChleHByX3Byb2Nlc3NlZCkKY29sbmFtZXMoZGYpIDwtIGlkX2FsbCRnZW5lLlNZTUJPTApsb2dfZHMgPC0gZGF0YS5mcmFtZShncm91cCwgZGYpCgojIEVuZDogRGF0YSBwcmVwcm9jZXNzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYAoKT25jZSB0aGUgcHJlcHJvY2Vzc2luZyBmaW5pc2hlZCwgd2UgZ290IHRoZSBwcm9jZXNzIGRhdGFzZXQgYXQgdGhlIHByb3RlaW4gbGV2ZWwsIGluIHdoaWNoIHRoZSBxdWFudGl0YXRpdmUgZGF0YSBhcmUgZXhwcmVzc2VkIGluIGxvZzIgdmFsdWVzLgpgYGB7cn0KcHJvY2Vzc19kcwpgYGAKClRoZSBkYXRhIHF1YWxpdHkgaXMgY2hlY2tlZCBieSBzZXZlcmFsIG1lYXN1cmVzLiBUaGUgZmlyc3Qgb25lIGlzICVjb2VmZmljaWVudCBvZiB2YXJpYXRpb24gKENWKS4KYGBge3IgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPVRSVUV9CiMgU3RhcnQ6IERhdGEgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMgR3JvdXAgYXZlcmFnZQp0bXAgPC0gZGF0YS5mcmFtZShncm91cCA9IGxvZ19kc1sgLCAxXSwgMl5sb2dfZHNbICwgMjpsZW5ndGgobG9nX2RzKV0pICU+JSAKICBnYXRoZXIoZ2VuZS5TWU1CT0wsIGV4cHJlc3Npb24sIC1ncm91cCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdyb3VwLCBnZW5lLlNZTUJPTCkgJT4lIAogIGRwbHlyOjpzdW1tYXJpemUoZ3JvdXBfbWVhbiA9IG1lYW4oZXhwcmVzc2lvbikpICU+JQogIHNwcmVhZChnZW5lLlNZTUJPTCwgZ3JvdXBfbWVhbikKZ3JfYXZyIDwtIGFzLmRhdGEuZnJhbWUodG1wWyAsIDI6bGVuZ3RoKHRtcCldKQpyb3duYW1lcyhncl9hdnIpIDwtIHRtcCRncm91cApncl9wYWlyIDwtIGNvbWJuKHVuaXF1ZSh0bXAkZ3JvdXApLCAyKSAJCmZjIDwtIChncl9hdnJbZ3JfcGFpclsxLCBdLCBdIC8gZ3JfYXZyW2dyX3BhaXJbMiwgXSwgXSkgJT4lIGxvZzIoKSAgCnJvd25hbWVzKGZjKSA8LSBwYXN0ZTAoJ2xvZzInLCAnKCcsIGdyX3BhaXJbMSwgXSwgJy8nLCBncl9wYWlyWzIsIF0sICcpJykKbG9nMmZjX2RzIDwtIGZjCgojIyBHcm91cCBTRAp0bXAgPC0gIGRhdGEuZnJhbWUoZ3JvdXAgPSBsb2dfZHNbICwgMV0sIDJebG9nX2RzWyAsIDI6bGVuZ3RoKGxvZ19kcyldKSAlPiUgCiAgZ2F0aGVyKGdlbmUuU1lNQk9MLCBleHByZXNzaW9uLCAtZ3JvdXApICU+JQogIGRwbHlyOjpncm91cF9ieShncm91cCwgZ2VuZS5TWU1CT0wpICU+JSAKICBkcGx5cjo6c3VtbWFyaXplKGdyb3VwX3NkID0gc2QoZXhwcmVzc2lvbikpICU+JQogIHNwcmVhZChnZW5lLlNZTUJPTCwgZ3JvdXBfc2QpCmdyX3NkIDwtIGFzLmRhdGEuZnJhbWUodG1wWyAsIDI6bGVuZ3RoKHRtcCldKQpyb3duYW1lcyhncl9zZCkgPC0gdG1wJGdyb3VwCgojIyBDb2VmZmljaWVudCBvZiB2YXJpYXRpb24KcWMgPC0gMTAwICpncl9zZC9ncl9hdnIgCnFjIDwtIGRhdGEuZnJhbWUoZ3JvdXAgPSB0bXAkZ3JvdXAsIHFjKQpRQyA8LSBxYyAlPiUgZ2F0aGVyKGdlbmUsIENWLCAtZ3JvdXApCgojIENhbGN1bGF0ZSBtZWRpYW4tQ1Ygb2YgZWFjaCBncm91cAptZWRpYW5DViA8LSBRQyAlPiUgZHBseXI6Omdyb3VwX2J5KGdyb3VwKSAlPiUgc3VtbWFyaXNlKENWID0gcm91bmQobWVkaWFuKENWKSwgMSkpCmBgYApNZWRpYW4tQ1ZzIGZvciBlYWNoIGdyb3VwOwpgYGB7cn0KcHJpbnQocGFzdGUwKCJNZWRpYW4tQ1Y6IFE3NDF4K2luaCwgIiwgbWVkaWFuQ1ZbMSwyXSwgIiU7IFdUK2luaCwgIiwgbWVkaWFuQ1ZbMiwyXSwgIiU7IFE3NDF4LCAiLCBtZWRpYW5DVlszLDJdLCAiJTsgV1QsICIsIG1lZGlhbkNWWzQsMl0sICIlIikpCmBgYAoKVmlvbGluIHBsb3Qgb2YgaW50ZXItZ3JvdXAgQ1YKYGBge3J9CiMgVmlvbGluIHBsb3Qgb2YgaW50ZXItZ3JvdXAgQ1YgCnBsb3QucWMgPC0gZ2dwbG90KFFDLCBhZXMoeD1ncm91cCwgeT1DVikpICsgCiAgICAgICAgICAgICAgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSBhcy5jaGFyYWN0ZXIoZ3JvdXApKSwgdHJpbT1GQUxTRSwgd2lkdGggPSAwLjgsICNhZXMoZmlsbCA9IGdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsIHBvc2l0aW9uID0gImRvZGdlIikrCiAgICAgICAgICAgICAgbGFicyhmaWxsID0gIiIpICsKICAgICAgICAgICAgICBnZW9tX2JveHBsb3Qod2lkdGg9MC4xLCBmaWxsID0gJ3doaXRlJywgb3V0bGllci5zaXplID0gMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFLCBwb3NpdGlvbiA9ICJkb2RnZSIpKwogICAgICAgICAgICAgIGdlb21fdGV4dChkYXRhID0gbWVkaWFuQ1YsIGFlcyhsYWJlbCA9IENWKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IC0wLjUsIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDUpICsKICAgICAgICAgICAgICB4bGFiKCIiKSArIHlsYWIoIiUgQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIikgKwogICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3M9YygwLCAxMCwgMjAsIDUwLCBjZWlsaW5nKG1heChRQyRDViwgbmEucm09VFJVRSkpKSkgKwogICAgICAgICAgICAgIHRoZW1lX2xpZ2h0KGJhc2Vfc2l6ZSA9IDEyKQpwbG90LnFjCiNwZGYoIlFDX3Zpb2xpblBsb3QucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQojcHJpbnQocGxvdC5xYykKI2Rldi5vZmYoKQpgYGAKSGVyZSBpcyB0aGUgc2NyaXB0OwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBWaW9saW4gcGxvdCBvZiBpbnRlci1ncm91cCBDViAKI3Bsb3QucWMgPC0gZ2dwbG90KFFDLCBhZXMoeD1ncm91cCwgeT1DVikpICsgCiAjICAgICAgICAgICAgIGdlb21fdmlvbGluKGFlcyhmaWxsID0gYXMuY2hhcmFjdGVyKGdyb3VwKSksIHRyaW09RkFMU0UsIHdpZHRoID0gMC44LCAjYWVzKGZpbGwgPSBncm91cCksCiAgIyAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSwgcG9zaXRpb24gPSAiZG9kZ2UiKSsKICAgIyAgICAgICAgICAgbGFicyhmaWxsID0gIiIpICsKICAgICMgICAgICAgICAgZ2VvbV9ib3hwbG90KHdpZHRoPTAuMSwgZmlsbCA9ICd3aGl0ZScsIG91dGxpZXIuc2l6ZSA9IDAsIAogICAgICMgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsIHBvc2l0aW9uID0gImRvZGdlIikrCiAgICAgICMgICAgICAgIGdlb21fdGV4dChkYXRhID0gbWVkaWFuQ1YsIGFlcyhsYWJlbCA9IENWKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDEpLCAKICAgICAgICMgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAtMC41LCB2anVzdCA9IC0wLjUsIHNpemUgPSA1KSArCiAgICAgICAgIyAgICAgIHhsYWIoIiIpICsgeWxhYigiJSBDb2VmZmljaWVudCBvZiBWYXJpYXRpb24iKSArCiAgICAgICAgICMgICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3M9YygwLCAxMCwgMjAsIDUwLCBjZWlsaW5nKG1heChRQyRDViwgbmEucm09VFJVRSkpKSkgKwogICAgICAgICAgIyAgICB0aGVtZV9saWdodChiYXNlX3NpemUgPSAxMikKI3Bsb3QucWMKYGBgCgoKQ29ycmVsYXRpb24gaGVhdG1hcCAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMgQ29ycmVsYXRpb24gaGVhdG1hcApjb3JyIDwtIDJeZXhwcl9wcm9jZXNzZWQKY29yciA8LSByb3VuZChjb3IoY29yciwgbWV0aG9kID0gInBlYXJzb24iKSwzKQpjb3JyW2xvd2VyLnRyaShjb3JyKV0gPC0gTkEKbWVsdGVkX2NvcnIgPC0gbWVsdChjb3JyLCBuYS5ybSA9IFRSVUUpCnBsb3RfY29yckhNIDwtIGdncGxvdChkYXRhID0gbWVsdGVkX2NvcnIsIGFlcyh4ID0gVmFyMiwgeSA9IFZhcjEsIGZpbGwgPSB2YWx1ZSkpKyAgCiAgICAgICAgICAgICAgICAgIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpKwogICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiLCBoaWdoID0gInJlZCIsIG1pZCA9ICJ5ZWxsb3ciLCAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaWRwb2ludCA9IDAuOSwgbGltaXQgPSBjKDAuOCwgMSksIHNwYWNlID0gIkxhYiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSBwYXN0ZSgiUGVhcnNvbiIsICJcbmNvcnJlbGF0aW9uIikgKSArCiAgICAgICAgICAgICAgICAgIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsKICAgICAgICAgICAgICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDgsIGhqdXN0ID0gMSkpICsKICAgICAgICAgICAgICAgICAgY29vcmRfZml4ZWQoKSArIAogICAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdmFsdWUpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSArICAKICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplPTgpLAogICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygxLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjYsIDAuNyksCiAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpKwogICAgICAgICAgICAgICAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2NvbG9yYmFyKGJhcndpZHRoID0gNywgYmFyaGVpZ2h0ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41KSkKCnBsb3RfY29yckhNCiNwZGYoInBsb3RfY29yckhNLnBkZiIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKI3ByaW50KHBsb3RfY29yckhNKQojZGV2Lm9mZigpCmBgYApIZXJlIGlzIHRoZSBzY3JpcHQ7CmBgYHtyfQojIyBDb3JyZWxhdGlvbiBoZWF0bWFwCiNjb3JyIDwtIDJeZXhwcl9wcm9jZXNzZWQKI2NvcnIgPC0gcm91bmQoY29yKGNvcnIsIG1ldGhvZCA9ICJwZWFyc29uIiksMykKI2NvcnJbbG93ZXIudHJpKGNvcnIpXSA8LSBOQQojbWVsdGVkX2NvcnIgPC0gbWVsdChjb3JyLCBuYS5ybSA9IFRSVUUpCiNwbG90X2NvcnJITSA8LSBnZ3Bsb3QoZGF0YSA9IG1lbHRlZF9jb3JyLCBhZXMoeCA9IFZhcjIsIHkgPSBWYXIxLCBmaWxsID0gdmFsdWUpKSsgIAogIyAgICAgICAgICAgICAgICAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIikrCiAgIyAgICAgICAgICAgICAgICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiLCBoaWdoID0gInJlZCIsIG1pZCA9ICJ5ZWxsb3ciLCAgIAogICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSAwLjksIGxpbWl0ID0gYygwLjgsIDEpLCBzcGFjZSA9ICJMYWIiLCAKICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IHBhc3RlKCJQZWFyc29uIiwgIlxuY29ycmVsYXRpb24iKSApICsKICAgICAjICAgICAgICAgICAgIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsKICAgICAgIyAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgICAjICAgICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDEsIAogICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDgsIGhqdXN0ID0gMSkpICsKICAgICAgICAgIyAgICAgICAgIGNvb3JkX2ZpeGVkKCkgKyAKICAgICAgICAgICAjICAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSB2YWx1ZSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIpICsgIAogICAgICAgICAgICAjICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplPTgpLAogICAgICAgICAgICAgIyAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAjICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgIyAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAjICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICMgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDApLAogICAgICAgICAgICAgICAgICAjICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjYsIDAuNyksCiAgICAgICAgICAgICAgICAgICAjICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSsKIyAgICAgICAgICAgICAgICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfY29sb3JiYXIoYmFyd2lkdGggPSA3LCBiYXJoZWlnaHQgPSAxLAogIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41KSkKCiNwbG90X2NvcnJITQpgYGAKClRoZSBwbG90IGZvciB0aGUgbnVtYmVycyBvZiBwZXB0aWRlIHBlciBwcm90ZWluIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBuUFAgcGxvdApuX3BlcHRfcHJvdCA8LSBhcmVhUGVwdCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFByb3RlaW4pICU+JSAKICBkcGx5cjo6c3VtbWFyaXplKG5fcGVwdCA9IG4oKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuX3BlcHQpKQpuUFAgPC0gZGF0YS5mcmFtZShuX3BlcHQgPSBjKCIxIiwgIjItNSIsICI2LTEwIiksIAogICAgICAgICAgICAgICAgICAgIG5fcHJvdCA9IHJiaW5kKG5fcGVwdF9wcm90ICU+JSBmaWx0ZXIobl9wZXB0ID09MSkgJT4lIG5yb3coKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3BlcHRfcHJvdCAlPiUgZmlsdGVyKG5fcGVwdCA+PTIgJiBuX3BlcHQgPD0gNSkgJT4lIG5yb3coKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9wZXB0X3Byb3QgJT4lIGZpbHRlcihuX3BlcHQgPj02KSAlPiUgbnJvdygpKSkKblBQX3Bsb3QgPC0gZ2dwbG90KG5QUCwgYWVzKHggPSBuX3BlcHQsIHk9IG5fcHJvdCkpICsgCiAgICAgICAgICAgICAgICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic3RlZWxibHVlIikgKyAKICAgICAgICAgICAgICAgICAgICB5bGltKDAsIG1heChuUFAkbl9wcm90KSs1MCkgKwogICAgICAgICAgICAgICAgICAgIGdlb21fdGV4dChhZXMobGFiZWw9IG5fcHJvdCksIHZqdXN0PS0wLjMsIGNvbG9yPSJibGFjayIsIHNpemU9NC41KSArCiAgICAgICAgICAgICAgICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD0gcGFzdGUwKHJvdW5kKDEwMCpuX3Byb3Qvc3VtKG5fcHJvdCksIDEpLCAiJSIpKSwgdmp1c3Q9MS42LCBjb2xvcj0id2hpdGUiLCBzaXplPTQuNSkgKwogICAgICAgICAgICAgICAgICAgIHhsYWIoIk51bWJlcnMgb2YgcGVwdGlkZSIpICsgeWxhYigiTnVtYmVycyBvZiBwcm90ZWluIikgKwogICAgICAgICAgICAgICAgICAgIHRoZW1lX2xpZ2h0KGJhc2Vfc2l6ZSA9IDEyKQpuUFBfcGxvdAojcGRmKCJudW1iZXJfcGVwdF9wcm90LnBkZiIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gMykKI3ByaW50KG5QUF9wbG90KQojZGV2Lm9mZigpCmBgYApIZXJlIGlzIHRoZSBzY3JpcHQ7CmBgYHtyfQojbl9wZXB0X3Byb3QgPC0gYXJlYVBlcHQgJT4lIAogIyBkcGx5cjo6Z3JvdXBfYnkoUHJvdGVpbikgJT4lIAogICNkcGx5cjo6c3VtbWFyaXplKG5fcGVwdCA9IG4oKSkgJT4lIAogICNhcnJhbmdlKGRlc2Mobl9wZXB0KSkKI25QUCA8LSBkYXRhLmZyYW1lKG5fcGVwdCA9IGMoIjEiLCAiMi01IiwgIjYtMTAiKSwgCiAjICAgICAgICAgICAgICAgICAgIG5fcHJvdCA9IHJiaW5kKG5fcGVwdF9wcm90ICU+JSBmaWx0ZXIobl9wZXB0ID09MSkgJT4lIG5yb3coKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9wZXB0X3Byb3QgJT4lIGZpbHRlcihuX3BlcHQgPj0yICYgbl9wZXB0IDw9IDUpICU+JSBucm93KCksIAogICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3BlcHRfcHJvdCAlPiUgZmlsdGVyKG5fcGVwdCA+PTYpICU+JSBucm93KCkpKQojblBQX3Bsb3QgPC0gZ2dwbG90KG5QUCwgYWVzKHggPSBuX3BlcHQsIHk9IG5fcHJvdCkpICsgCiAjICAgICAgICAgICAgICAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInN0ZWVsYmx1ZSIpICsgCiAgIyAgICAgICAgICAgICAgICAgIHlsaW0oMCwgbWF4KG5QUCRuX3Byb3QpKzUwKSArCiAgICMgICAgICAgICAgICAgICAgIGdlb21fdGV4dChhZXMobGFiZWw9IG5fcHJvdCksIHZqdXN0PS0wLjMsIGNvbG9yPSJibGFjayIsIHNpemU9NC41KSArCiAgICAjICAgICAgICAgICAgICAgIGdlb21fdGV4dChhZXMobGFiZWw9IHBhc3RlMChyb3VuZCgxMDAqbl9wcm90L3N1bShuX3Byb3QpLCAxKSwgIiUiKSksIHZqdXN0PTEuNiwgY29sb3I9IndoaXRlIiwgc2l6ZT00LjUpICsKICAgICAjICAgICAgICAgICAgICAgeGxhYigiTnVtYmVycyBvZiBwZXB0aWRlIikgKyB5bGFiKCJOdW1iZXJzIG9mIHByb3RlaW4iKSArCiAgICAgICMgICAgICAgICAgICAgIHRoZW1lX2xpZ2h0KGJhc2Vfc2l6ZSA9IDEyKQojblBQX3Bsb3QKYGBgCgpQQ0EgaW5kaXZpZHVhbCBwbG90IApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBQQ0EgaW5kaXZpZHVhbCBwbG90IHVzaW5nIEZhY3Rvck1pbmVSIHBhY2thZ2UgKHJlZiMzKQpmaXRfcGNhIDwtIFBDQShsb2dfZHNbICwgMjpsZW5ndGgobG9nX2RzKV0sIGdyYXBoID0gRkFMU0UsIHNjYWxlLnVuaXQgPSBUUlVFKQpwZXJjZW50YWdlIDwtIGZpdF9wY2EkZWlnWyAsIDJdClBDcyA8LSBkYXRhLmZyYW1lKGZpdF9wY2EkaW5kJGNvb3JkKQpQQ3MkZ3JvdXAgPC0gYXMuY2hhcmFjdGVyKGdyb3VwKSAKcGxvdFBDQSA8LSBnZ3Bsb3QoZGF0YSA9IFBDcywgYWVzKHggPSBEaW0uMSwgeSA9IERpbS4yKSkgKwogICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGdyb3VwKSwgc2l6ZSA9IDMpICsKICAgICAgICAgICAgICBsYWJzKGNvbG91ciA9ICcnKSArCiAgICAgICAgICAgICAgeGxhYihwYXN0ZTAoJ1BDMScsICcgJywgJygnLCByb3VuZChwZXJjZW50YWdlWzFdLCAyKSwgJyUpJykpICsgCiAgICAgICAgICAgICAgeWxhYihwYXN0ZTAoJ1BDMicsICcgJywgJygnLCByb3VuZChwZXJjZW50YWdlWzJdLCAyKSwgJyUpJykpICsKICAgICAgICAgICAgICBzY2FsZV9maWxsX2h1ZShsPTQwKSArIAogICAgICAgICAgICAgIGNvb3JkX2ZpeGVkKHJhdGlvPTEsIHhsaW09cmFuZ2UoUENzJERpbS4xKSwgeWxpbT1yYW5nZShQQ3MkRGltLjIpKSArCiAgICAgICAgICAgICAgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsID0gcm93bmFtZXMoUENzKSkgKwogICAgICAgICAgICAgIHRoZW1lX2xpZ2h0KGJhc2Vfc2l6ZSA9IDE1KQpwbG90UENBCiNwZGYoInBsb3RQQ0EucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQojcHJpbnQocGxvdFBDQSkKI2Rldi5vZmYoKQpgYGAKSGVyZSBpcyB0aGUgc2NyaXB0OwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBQQ0EgaW5kaXZpZHVhbCBwbG90IHVzaW5nIEZhY3Rvck1pbmVSIHBhY2thZ2UgKHJlZiMzKQojZml0X3BjYSA8LSBQQ0EobG9nX2RzWyAsIDI6bGVuZ3RoKGxvZ19kcyldLCBncmFwaCA9IEZBTFNFLCBzY2FsZS51bml0ID0gVFJVRSkKI3BlcmNlbnRhZ2UgPC0gZml0X3BjYSRlaWdbICwgMl0KI1BDcyA8LSBkYXRhLmZyYW1lKGZpdF9wY2EkaW5kJGNvb3JkKQojUENzJGdyb3VwIDwtIGFzLmNoYXJhY3Rlcihncm91cCkgCiNwbG90UENBIDwtIGdncGxvdChkYXRhID0gUENzLCBhZXMoeCA9IERpbS4xLCB5ID0gRGltLjIpKSArCiAjICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGdyb3VwKSwgc2l6ZSA9IDMpICsKICAjICAgICAgICAgICAgbGFicyhjb2xvdXIgPSAnJykgKwogICAjICAgICAgICAgICB4bGFiKHBhc3RlMCgnUEMxJywgJyAnLCAnKCcsIHJvdW5kKHBlcmNlbnRhZ2VbMV0sIDIpLCAnJSknKSkgKyAKICAgICMgICAgICAgICAgeWxhYihwYXN0ZTAoJ1BDMicsICcgJywgJygnLCByb3VuZChwZXJjZW50YWdlWzJdLCAyKSwgJyUpJykpICsKICAgICAjICAgICAgICAgc2NhbGVfZmlsbF9odWUobD00MCkgKyAKICAgICAgIyAgICAgICAgY29vcmRfZml4ZWQocmF0aW89MSwgeGxpbT1yYW5nZShQQ3MkRGltLjEpLCB5bGltPXJhbmdlKFBDcyREaW0uMikpICsKICAgICAgICMgICAgICAgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsID0gcm93bmFtZXMoUENzKSkgKwogICAgICAgICMgICAgICB0aGVtZV9saWdodChiYXNlX3NpemUgPSAxNSkKI3Bsb3RQQ0EKYGBgCgpOb3RlIHRoYXQgdGhlIGNvbnRyaWJ1dGlvbnMgb2YgcHJvdGVpbiB2YXJpYWJsZXMgb2YgZWFjaCBjb21wb25lbnQgY2FuIGJlIGV4dHJhY3RlZCBmcm9tIHRoZSBmaXRfcGNhIG9iamVjdCBmb3IgaW4tZGVwdGggYmlvbG9naWNhbCBpbnRlcnByZXRhdGlvbi4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YS5mcmFtZShmaXRfcGNhW1sidmFyIl1dW1siY29udHJpYiJdXSkKYGBgCgpMYXN0bHksIHRoZSBwcm90ZWluIGFidW5kYW5jZSBoZWF0bWFwICh2YWx1ZXMgaW4gdGhlIGxvZzEwIHNjYWxlKSB3aGVyZSB0aGUgbWlzc2luZyB2YWx1ZXMgYXJlIG1hcHBlZCBpbiBibGFjayBjb2xvci4KYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBQcm90ZWluIGFidW5kYW5jZSBoZWF0bWFwIGJ5IHBoZWF0bWFwIHBhY2thZ2UgKHJlZiM0KQpxY19obSA8LSAyXmV4cHJfcHJvY2Vzc2VkCnJvd25hbWVzKHFjX2htKSA8LSBwcm9jZXNzX2RzJGdlbmUuU1lNQk9MIApmb3IoaSBpbiBzZXFfYWxvbmcocWNfaG0pKXsKICBpZihxY19obVtpXSAhPSAwKXsKICAgIHFjX2htW2ldIDwtIGxvZzEwKHFjX2htW2ldKQogIH0gZWxzZSB7CiAgICBxY19obVtpXSA8LSAwCiAgfX0gICAgCm5fbWlzc2luZyA8LSBzdW0ocWNfaG0gPT0gMCkKbl90b3RhbCA8LSBkaW0ocWNfaG0pWzFdICogZGltKHFjX2htKVsyXQpgYGAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcHJpbnQocGFzdGUwKCJRQ19oZWF0bWFwOiBUb3RhbCAiLCBuX3RvdGFsLCAiIGRhdGEgcG9pbnRzOyAiLCBuX21pc3NpbmcsICIgbWlzc2luZyB2YWx1ZXMgKCIsIHJvdW5kKDEwMCpuX21pc3Npbmcvbl90b3RhbCwgMiksICIlKSBzaG93ZWQgaW4gYmxhY2spIikpCgpgYGAKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcWNfaG1fcGxvdCA8LSBwaGVhdG1hcCh0KHFjX2htKSwgIAogICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIG1heChxY19obSksIGxlbmd0aC5vdXQ9MTAxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRfYnJlYWtzID0gc2VxKDAsIHJvdW5kKG1heChxY19obSksIDApLCBsZW5ndGgub3V0PTgpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF9sYWJlbHMgPSBjKCIxZSswMCIsICIxZSswMSIsICIxZSswMiIsICIxZSswMyIsICIxZSswNCIsICIxZSswNSIsICIxZSswNiIsICIxZSswNyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoImJsYWNrIiwgIiM4ZWExZmYiLCAiIzE0ZmY1NyIsICJ5ZWxsb3ciLCAJIm9yYW5nZSIsICIjZWE0NDQ0IikpKDEwMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgYm9yZGVyX2NvbG9yID0gImdyYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gIm1heGltdW0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kX2NvbHVtbnMgPSAiY29tcGxldGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwgZm9udHNpemVfY29sID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJub25lIikKICAgICAgICAgICAgICAgICAgICAgICAgICNtYWluID0gcGFzdGUwKCJUb3RhbCAiLCBuX3RvdGFsLCAiIGRhdGEgcG9pbnRzOyAiLCBuX21pc3NpbmcsICIgbWlzc2luZyB2YWx1ZXMgKCIsIHJvdW5kKDEwMCpuX21pc3Npbmcvbl90b3RhbCwgMiksICIlKSBzaG93ZWQgaW4gYmxhY2spIikgKQojcWNfaG1fcGxvdAojcGRmKCJxY19oZWF0bWFwLnBkZiIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNCkKI3ByaW50KHFjX2htX3Bsb3QpCiNkZXYub2ZmKCkKYGBgCkhlcmUgaXMgdGhlIHNjcmlwdDsKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBQcm90ZWluIGFidW5kYW5jZSBoZWF0bWFwIGJ5IHBoZWF0bWFwIHBhY2thZ2UgKHJlZiM0KQojcWNfaG0gPC0gMl5leHByX3Byb2Nlc3NlZAojcm93bmFtZXMocWNfaG0pIDwtIHByb2Nlc3NfZHMkZ2VuZS5TWU1CT0wgCiNmb3IoaSBpbiBzZXFfYWxvbmcocWNfaG0pKXsKICMgaWYocWNfaG1baV0gIT0gMCl7CiAgIyAgcWNfaG1baV0gPC0gbG9nMTAocWNfaG1baV0pCiMgIH0gZWxzZSB7CiAjICAgcWNfaG1baV0gPC0gMAogICN9fSAgICAKI25fbWlzc2luZyA8LSBzdW0ocWNfaG0gPT0gMCkKI25fdG90YWwgPC0gZGltKHFjX2htKVsxXSAqIGRpbShxY19obSlbMl0KCiNxY19obV9wbG90IDwtIHBoZWF0bWFwKHQocWNfaG0pLCAgCiAjICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIG1heChxY19obSksIGxlbmd0aC5vdXQ9MTAxKSwgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IHNlcSgwLCByb3VuZChtYXgocWNfaG0pLCAwKSwgbGVuZ3RoLm91dD04KSwgCiAgICMgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xhYmVscyA9IGMoIjFlKzAwIiwgIjFlKzAxIiwgIjFlKzAyIiwgIjFlKzAzIiwgIjFlKzA0IiwgIjFlKzA1IiwgIjFlKzA2IiwgIjFlKzA3IiksCiAgICAjICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoImJsYWNrIiwgIiM4ZWExZmYiLCAiIzE0ZmY1NyIsICJ5ZWxsb3ciLCAJIm9yYW5nZSIsICIjZWE0NDQ0IikpKDEwMCksIAogICAgICMgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9ICJncmF5IiwKICAgICAgIyAgICAgICAgICAgICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAibWF4aW11bSIsIAogICAgICAgIyAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kX2NvbHVtbnMgPSAiY29tcGxldGUiLAogICAgICAgICMgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCAKICAgICAgICAgIyAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LCBmb250c2l6ZV9jb2wgPSAxLCAKICAgICAgICAgICMgICAgICAgICAgICAgICBzY2FsZSA9ICJub25lIikKICAgICAgICAgICAjICAgICAgICAgICAgICAjbWFpbiA9IHBhc3RlMCgiVG90YWwgIiwgbl90b3RhbCwgIiBkYXRhIHBvaW50czsgIiwgbl9taXNzaW5nLCAiIG1pc3NpbmcgdmFsdWVzICgiLCByb3VuZCgxMDAqbl9taXNzaW5nL25fdG90YWwsIDIpLCAiJSkgI3Nob3dlZCBpbiBibGFjaykiKSApCiNxY19obV9wbG90CmBgYAoKCkRpZmZlcm50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgZm9yIG11bHRpcGxlIGdyb3VwIGNvbXBhcmlzb24gaXMgcGVyZm9ybWVkIGJ5IEFOT1ZBIHdpdGggVHVrZXkncyBwb3N0LWhvYy4gCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIEFOT1ZBIHdpdGggVHVrZXkncyBwb3N0LWhvYwp0bXAgPC0gYXMubWF0cml4KGxvZ19kc1ssIDI6bGVuZ3RoKGxvZ19kcyldKQpmaXQuYW92IDwtIGFvdih0bXAgfiBncm91cCkgCm91dHB1dC5hb3YgPC0gc3VtbWFyeS5hb3YoZml0LmFvdikgCmFub3ZhLnBWYWwgPC0gbnVtZXJpYyhsZW5ndGggPSBuY29sKHRtcCkpCmZvciAoaSBpbiAxOmxlbmd0aChvdXRwdXQuYW92KSl7CiAgYW5vdmEucFZhbFtpXSA8LSBvdXRwdXQuYW92W1tpXV1bMSwgNV0KfQphZGoucFZhbCA8LSBtYXRyaXgobnJvdyA9IG5jb2wodG1wKSwgbmNvbCA9IG5yb3cobG9nMmZjX2RzKSkKY29sbmFtZXMoYWRqLnBWYWwpIDwtICBwYXN0ZShncl9wYWlyWzEsIF0sICIgdnMgIiwgZ3JfcGFpclsyLCBdKQpyb3duYW1lcyhhZGoucFZhbCkgPC0gY29sbmFtZXModG1wKQpmb3IgKGkgaW4gMTpuY29sKHRtcCkpeyAKICBhZGoucFZhbFtpLCBdIDwtIChUdWtleUhTRCgoYW92KHRtcFssIGldIH4gZ3JvdXApKSkpW1sxXV1bICw0XSAKfQphbm92YV9kcyA8LSBjYmluZChhbm92YS5wVmFsLCBhZGoucFZhbCkKYGBgClRoZSBBTk9WQSBwLXZhbHVlcyAodGhlIGZpcnN0IGNvbHVtbikgYW5kIHRoZSBhZGp1c3RlZCBwLXZhbHVlcyBmcm9tIFR1a2V5J3MgcG9zdGhvYyBmb3IgZWFjaCBwYWlyIChsYWJlbGxlZCBhcyB0aGUgY29sdW1uIG5hbWUpIGFyZSByZWFkeSBmb3IgZnVydGhlciBhbmFseXNlcy4gCmBgYHtyfQpkYXRhLmZyYW1lKGFub3ZhX2RzKQpgYGAKCkRhdGEgaW5jbHVkaW5nIHRoZSBmb2xkIGNoYW5nZXMgYW5kIHRoZSBhZGp1c3RlZCBwLXZhbHVlcyBvZiBwcm90ZWlucyBpbiBlYWNoIHBhaXJ3aXNlIGFyZSByZWFkeSBmb3IgdGhlIHZvbGNhbm8gcGxvdHMuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIFBhaXJ3aXNlLVZvbGNhbm8gcGxvdAp0bXAgPC0gZGF0YS5mcmFtZShnZW5lID0gcm93bmFtZXMoYW5vdmFfZHMpLCBhbm92YV9kcykgICAKY29sbmFtZXModG1wKSA8LSBjKCJnZW5lIiwgImFub3ZhLnBWYWwiLCBwYXN0ZTAoZ3JfcGFpclsxLCBdLCAiLyIsIGdyX3BhaXJbMiwgXSkgKQpsb25nX2FubyA8LSBnYXRoZXIodG1wLCBjb21wYXJlLCBhZGpfcFZhbCwgLWdlbmUsIC1hbm92YS5wVmFsKSAgICAgCmZjLnZwIDwtIHQobG9nMmZjX2RzKQpmYy52cCA8LSBkYXRhLmZyYW1lKGdlbmUgPSBjb2xuYW1lcyhsb2cyZmNfZHMpLCBmYy52cCkKY29sbmFtZXMoZmMudnApIDwtIGMoImdlbmUiLCBwYXN0ZTAoZ3JfcGFpclsxLCBdLCAiLyIsIGdyX3BhaXJbMiwgXSkgKQpsb25nX2ZjIDwtIGdhdGhlcihmYy52cCwgY29tcGFyZSwgbG9nMkZDLCAtZ2VuZSkKbG9uZ19hbm8uZmMgPC0gbG9uZ19hbm8gJT4lIAogIGxlZnRfam9pbihsb25nX2ZjLCBieSA9IGMoImdlbmUiLCAiY29tcGFyZSIpKQpsb25nX2Fuby5mYyRnZW5lIDwtIGFzLmNoYXJhY3Rlcihsb25nX2Fuby5mYyRnZW5lKQpgYGAKCkhlcmUgaXMgdGhlIG11bGl0cGxlIHBhaXJ3aXNlIHZvbGNhbm8gcGxvdHMsIHdoZXJlIHRoZSByZWQgZG90cyByZXByZXNlbnQgdGhlIHJlbGV2YW50IHByb3RlaW5zIGJhc2VkIG9uIHRoZSB0aHJlc2hvbGRzIG9mID4xLjV4IGZvbGQgY2hhbmdlIGFuZCB0aGUgYWRqdXN0ZWQgcC12YWx1ZSA8MC4wNTsKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kdm9sY2Fub19hbGwgPC0gZ2dwbG90KGRhdGEgPSBsb25nX2Fuby5mYywgYWVzKHg9IGxvZzJGQywgeT0tbG9nMTAoYWRqX3BWYWwpKSkgKwogICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYXMuZmFjdG9yKGFicyhsb2cyRkMpID49IGxvZzIoMS41KSAmCWFub3ZhLnBWYWwgPCAwLjA1ICYgYWRqX3BWYWwgPCAwLjA1KSksIHNpemUgPSAzLCAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAjYWxwaGEgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImdyZXkiLCAicmVkIikpICsKICAgICAgICAgICAgICAgICAgICB4bGFiKCJsb2cyIChmb2xkIGNoYW5nZSkiKSArIHlsYWIoIi1sb2cxMCAoYWRqdXN0ZWQgcC12YWx1ZSkiKSArCiAgICAgICAgICAgICAgICAgICAjIGdndGl0bGUobGFiZWwgPSBwYXN0ZTAoIlZvbGNhbm8gcGxvdCBhdCAiLCAxLjUsIAogICAgICAgICAgICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICJ4IGZvbGQgY2hhbmdlIGFuZCBhZGp1c3RlZCBQLXZhbHVlIDwgIiwgMC4wNSkpICsgCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfZ3JleShiYXNlX3NpemUgPSAxNSkgKwogICAgICAgICAgICAgICAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gKHN1YnNldChsb25nX2Fuby5mYywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFicyhsb2cyRkMpID4gMiAmIC1sb2cxMChhZGpfcFZhbCkgPiAxLjMzIHwgLWxvZzEwKGFkal9wVmFsKSA+IDYpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gZ2VuZSwgc2l6ZSA9IDAuMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICdkYXJrYmx1ZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBib3gucGFkZGluZyA9IHVuaXQoMC4zNSwgImxpbmVzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSB1bml0KDAuNSwgImxpbmVzIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgICAgICAgICAgICAgICAgICAgZmFjZXRfd3JhcCh+IGNvbXBhcmUpCnZvbGNhbm9fYWxsCiNwcmludChwYXN0ZTAoIlZvbGNhbm9fcGxvdDogdGhyZXNob2xkcyBhdCAiLCAxLjUsICJ4IGZvbGQgY2hhbmdlIGFuZCBhZGp1c3RlZCBQLXZhbHVlIDwgIiwgMC4wNSkpCiNwZGYoInZvbGNhbm9fYWxsLnBkZiIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDYpCiNwcmludCh2b2xjYW5vX2FsbCkKI2Rldi5vZmYoKQpgYGAKQW5kIHRoZSBzY3JpcHQ7CmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiN2b2xjYW5vX2FsbCA8LSBnZ3Bsb3QoZGF0YSA9IGxvbmdfYW5vLmZjLCBhZXMoeD0gbG9nMkZDLCB5PS1sb2cxMChhZGpfcFZhbCkpKSArCiAjICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYXMuZmFjdG9yKGFicyhsb2cyRkMpID49IGxvZzIoMS41KSAmCWFub3ZhLnBWYWwgPCAwLjA1ICYgYWRqX3BWYWwgPCAwLjA1KSksIHNpemUgPSAzLCAgc2hvdy5sZWdlbmQgPSAjRkFMU0UpICsgI2FscGhhID0gMC41LAogIyAgICAgICAgICAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JleSIsICJyZWQiKSkgKwogICMgICAgICAgICAgICAgICAgICB4bGFiKCJsb2cyIChmb2xkIGNoYW5nZSkiKSArIHlsYWIoIi1sb2cxMCAoYWRqdXN0ZWQgcC12YWx1ZSkiKSArCiAgICMgICAgICAgICAgICAgICAgIyBnZ3RpdGxlKGxhYmVsID0gcGFzdGUwKCJWb2xjYW5vIHBsb3QgYXQgIiwgMS41LCAKICAgICMgICAgICAgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgInggZm9sZCBjaGFuZ2UgYW5kIGFkanVzdGVkIFAtdmFsdWUgPCAiLCAwLjA1KSkgKyAKICAgICAjICAgICAgICAgICAgICAgdGhlbWVfZ3JleShiYXNlX3NpemUgPSAxNSkgKwogICAgICAjICAgICAgICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IChzdWJzZXQobG9uZ19hbm8uZmMsIAogICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWJzKGxvZzJGQykgPiAyICYgLWxvZzEwKGFkal9wVmFsKSA+IDEuMzMgfCAtbG9nMTAoYWRqX3BWYWwpID4gNikpLAogICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gZ2VuZSwgc2l6ZSA9IDAuMSksCiAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gJ2RhcmtibHVlJywKICAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgIyBib3gucGFkZGluZyA9IHVuaXQoMC4zNSwgImxpbmVzIiksCiAgICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gdW5pdCgwLjUsICJsaW5lcyIpCiAgICAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICApICsKICAgICAgICAgICAgICAjICAgICAgZmFjZXRfd3JhcCh+IGNvbXBhcmUpCiN2b2xjYW5vX2FsbApgYGAKCgpUaGUgbGlzdHMgb2YgcmVsZXZhbnQgcHJvdGVpbnMgY2FuIGJlIGV4dHJhY3RlZCBmcm9tIHRoZSBsb25nLmFuby5mYyBvYmplY3Q7CmBgYHtyIGVjaG89VFJVRX0KbG9uZ19hbm8uZmMKYGBgCgpGb3IgZXhhbXBsZSwgYSBsaXN0IG9mIDYxIHJlbGV2ZW50IHByb3RlaW5zIChpbiBnZW5lIG5hbWVzKSBjYW4gYmUgZXh0cmFjdGVkIGZyb20gdGhlIFE3NDF4K2luaCB2cy4gUTc0MXggY29tcGFyaXNpb24gYXQgdGhlIHRocmVzaG9sZCBvZiA+MS41eCBmb2xkIGNoYW5nZXMgYW5kIGFkai5wVmFsIDwgMC4wNS4gVGhlIHByb3RlaW4gbGlzdCBjYW4gYmUgdXNlZCBmb3IgZnVydGhlciBiaW9sb2dpY2FsIGludGVycHJldGF0aW9uOwpgYGB7cn0KbG9uZ19hbm8uZmMgJT4lIGZpbHRlcihjb21wYXJlID09ICJRNzQxeCtpbmgvUTc0MXgiKSAlPiUgZmlsdGVyKGFicyhsb2cyRkMpID49IGxvZzIoMS41KSkgJT4lIGZpbHRlcihhZGpfcFZhbCA8IDAuMDUpICU+JSAuJGdlbmUKCmBgYAoKCkZpbmFsbHksIHRoZSBzaWduaWZpY2FudCBwcm90ZWluIGhlYXRtYXAgZGVtb25zdHJhdGVkIHNldmVyYWwgcHJvdGVpbiBjbHVzdGVycyBkaXN0aW5jdCB0byBlYWNoIHRyZWF0bWVudCBjb25kaXRpb25zIHdoaWNoIGNhbiBiZSB1c2VkIGxhdGVyIGZvciBpbi1kZXB0aCBiaW9sb2dpY2FsIGludGVycHJldGF0aW9uLiBUaGUgaGVhdG1hcCBpcyBwbG90dGVkIHVzaW5nIHRoZSBwaGVhdG1hcCBmdW5jdGlvbiBvZiBwaGVhdG1hcCBwYWNrYWdlLiAKYGBge3IgZmlnLmhlaWdodD00LjUsIGZpZy53aWR0aD0zLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1UUlVFfQojIyBTaWduaWZpY2FudCBwcm90ZWluIGhlYXRtYXAgYnkgcGhlYXRtYXAgKHJlZiM0KQp0bXAgPC0gYXMubWF0cml4KGxvZ19kc1sgLCAyOmxlbmd0aChsb2dfZHMpXSkKbWVkIDwtIGFwcGx5KHQodG1wKSwgMSwgbWVhbikKbWVkU2NhbGUgPC0gKHQodG1wKSAtIG1lZCkKdG1wIDwtIGFub3ZhX2RzWywgMV0KbWVkU2NhbGUgPC0gZGF0YS5mcmFtZShtZWRTY2FsZSwgCiAgICAgICAgICAgICAgICAgICAgICBhbm92YV9wVmFsID0gdG1wLCAKICAgICAgICAgICAgICAgICAgICAgIGdlbmUgPSByb3duYW1lcyhtZWRTY2FsZSkpCmNvbG5hbWVzKG1lZFNjYWxlKSA8LSBjKCJXVF8xIiwgIldUXzIiLCAiV1RfMyIsICJXVCtpbmhfMSIsICJXVCtpbmhfMiIsICJXVCtpbmhfMyIsICJRNzQxeF8xIiwgIlE3NDF4XzIiLCAiUTc0MXhfMyIsICJRNzQxeCtpbmhfMSIsICJRNzQxeCtpbmhfMiIsICJRNzQxeCtpbmhfMyIsICJhbm92YV9wVmFsIiwgImdlbmUiKQptZWRTY2FsZV9zaWcgPC0gbWVkU2NhbGUgJT4lIGZpbHRlcihhbm92YV9wVmFsIDwgMC4wNSkKcm93bmFtZXMobWVkU2NhbGVfc2lnKSA8LSBtZWRTY2FsZV9zaWckZ2VuZQptZWRTY2FsZV9zaWcgPC0gbWVkU2NhbGVfc2lnWywgMTogKGxlbmd0aChtZWRTY2FsZV9zaWcpIC0gMildCm5wcm90X3NpZyA8LSBucm93KG1lZFNjYWxlX3NpZykKZ3JvdXAgPC0gZmFjdG9yKGdyb3VwLCBvcmRlcmVkID0gVFJVRSwgCiAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJXVCtpbmgiLCAiUTc0MXgraW5oIiwgIlE3NDF4IiwgIldUIikpCmhtX3NpZyA8LSBwaGVhdG1hcChtZWRTY2FsZV9zaWcsIHNpbGVudCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLShtYXgocm91bmQobWVkU2NhbGVfc2lnLCAwKSkpLCBtYXgocm91bmQobWVkU2NhbGVfc2lnLCAwKSksIGxlbmd0aC5vdXQ9MTAxKSwgCiAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF9icmVha3MgPSBzZXEoLShtYXgocm91bmQobWVkU2NhbGVfc2lnLCAwKSkpLCBtYXgocm91bmQobWVkU2NhbGVfc2lnLCAwKSksIGxlbmd0aC5vdXQ9NSksCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJkYXJrYmx1ZSIsICJibHVlIiwgIndoaXRlIiwgIm9yYW5nZXJlZCIsICJyZWQiKSkoMTAwKSwgIAogICAgICAgICAgICAgICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBkYXRhLmZyYW1lKGdyb3VwID0gZ3JvdXAsICNmYWN0b3IoZ3JvdXApLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IHNhbXBsZV9sYWJlbCksCiAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICBjbHVzdGVyaW5nX21ldGhvZCA9ICJhdmVyYWdlIiwgCiAgICAgICAgICAgICAgICAgICAgIGZvbnRzaXplX3JvdyA9IDIsIGZvbnRzaXplX2NvbCA9IDEwLCAKICAgICAgICAgICAgICAgICAgICAgc2NhbGUgPSAibm9uZSIpCiAgICAgICAgICAgICAgICAgICAgICNtYWluID0gcGFzdGUwKG5wcm90X3NpZywgIiBzaWduaWZpY2FudCBwcm90ZWlucyAoQU5PVkEgcC12YWx1ZSA8ICIsIDAuMDUsICIpIiwgIlxuU2NhbGU6IExvZzIoZm9sZCBjaGFuZ2UpIHdpdGggbWVhbiBjZW50ZXIiLCAiXG5DbHVzdGVyaW5nOiBDb3JyZWxhdGlvbiBkaXN0YW5jZSBhbmQgYXZlcmFnZSBsaW5rYWdlIikpCiNobV9zaWcKYGBgCgpUaGUgaGVhdG1hcCBwYXJhbWV0ZXJzIHByb3ZpZGVkIGJlbG93IGFyZSBqdXN0IGZvciBhIHJlcHJvZHVjaWJpbGl0eSBwdXJwb3NlLgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcmludChwYXN0ZTAoIlNpZ25pZmljYW50IHByb3RlaW4gaGVhdG1hcDoiLCBucHJvdF9zaWcsICIgc2lnbmlmaWNhbnQgcHJvdGVpbnMgKEFOT1ZBIHAtdmFsdWUgPCAiLCAwLjA1LCAiKSIsICI7IFNjYWxlOiBMb2cyKGZvbGQgY2hhbmdlKSB3aXRoIG1lYW4gY2VudGVyIiwgIjsgQ2x1c3RlcmluZzogQ29ycmVsYXRpb24gZGlzdGFuY2UgYW5kIGF2ZXJhZ2UgbGlua2FnZSIpKQojcGRmKCJoZWF0bWFwX3NpZy5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDkpCiNwcmludChobV9zaWcpCiNkZXYub2ZmKCkKCmBgYAoKVGhpcyBpcyB0aGUgZW5kIG9mIHRoZSBzY3JpcHQuIFRoYW5rIHlvdS4KYGBge3J9CiMgRW5kOiBEYXRhIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFJlZmVyZW5jZXMKIyMgMS4gRHVyaW5jayBTLCBTcGVsbG1hbiBQLCBCaXJuZXkgRSwgSHViZXIgVyAoMjAwOSkuIOKAnE1hcHBpbmcgaWRlbnRpZmllcnMgZm9yIHRoZSBpbnRlZ3JhdGlvbiBvZiBnZW5vbWljIGRhdGFzZXRzIHdpdGggdGhlIFIvQmlvY29uZHVjdG9yIHBhY2thZ2UgYmlvbWFSdC7igJ0gTmF0dXJlIFByb3RvY29scywgNCwgMTE4NOKAkzExOTEuCiMjIDIuIEJvbHN0YWQgQiAoMjAxOCkuIHByZXByb2Nlc3NDb3JlOiBBIGNvbGxlY3Rpb24gb2YgcHJlLXByb2Nlc3NpbmcgZnVuY3Rpb25zLiBSIHBhY2thZ2UgdmVyc2lvbiAxLjQ0LjAsIAojIyAzLiBMw6osIFMuLCBKb3NzZSwgSi4gJiBIdXNzb24sIEYuICgyMDA4KS4gRmFjdG9NaW5lUjogQW4gUiBQYWNrYWdlIGZvciBNdWx0aXZhcmlhdGUgQW5hbHlzaXMuIEpvdXJuYWwgb2YgU3RhdGlzdGljYWwgU29mdHdhcmUuIDI1KDEpLiBwcC4gMS0xOC4KIyMgNC4gUmFpdm8gS29sZGUgKDIwMTgpLiBwaGVhdG1hcDogUHJldHR5IEhlYXRtYXBzLiBSIHBhY2thZ2UgdmVyc2lvbiAxLjAuMTAuIAoKYGBgCgoKQWRkaXRpb25hbCBhbmFseXNpcyMxOiAlY29lZmZpY2llbnQgb2YgdmFyaWF0aW9uIG9mIHBlcHRpZGUgcmV0ZW50aW9uIHRpbWUgKFJUKQpgYGB7cn0KUlQgPC0gcmVhZF9leGNlbChkYXRhX3BhdGgsIHNoZWV0ID0gIk9ic2VydmVkIFJUIikKUlQgPC0gUlQgJT4lIGZpbHRlcihEZWNveSA9PSAiRkFMU0UiKQpSVCA8LSBSVFssIGMoMiwgODpsZW5ndGgoUlQpKV0KY29sbmFtZXMoUlQpIDwtIGMoIlBlcHRpZGVzIiwgc2FtcGxlX2xhYmVsKQp0UlQgPC0gdChSVFssIDI6bGVuZ3RoKFJUKV0pCmNvbG5hbWVzKHRSVCkgPC0gUlQkUGVwdGlkZXMKdFJUIDwtIGRhdGEuZnJhbWUoZ3JvdXAsIHRSVCkKCiMjIEdyb3VwIFJUIGF2ZXJhZ2UKdG1wX1JUIDwtIHRSVCAlPiUgCiAgZ2F0aGVyKFBlcHRpZGVzLCBSVCwgLWdyb3VwKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZ3JvdXAsIFBlcHRpZGVzKSAlPiUgCiAgZHBseXI6OnN1bW1hcml6ZShncm91cF9tZWFuID0gbWVhbihSVCkpICU+JQogIHNwcmVhZChQZXB0aWRlcywgZ3JvdXBfbWVhbikKZ3JfUlRfYXZyIDwtIGFzLmRhdGEuZnJhbWUodG1wX1JUWyAsIDI6bGVuZ3RoKHRtcF9SVCldKQpyb3duYW1lcyhncl9SVF9hdnIpIDwtIHRtcF9SVCRncm91cAoKIyMgR3JvdXAgUlQgU0QKdG1wX1JUIDwtICB0UlQgJT4lIAogIGdhdGhlcihQZXB0aWRlcywgUlQsIC1ncm91cCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdyb3VwLCBQZXB0aWRlcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpemUoZ3JvdXBfc2QgPSBzZChSVCkpICU+JQogIHNwcmVhZChQZXB0aWRlcywgZ3JvdXBfc2QpCmdyX1JUX3NkIDwtIGFzLmRhdGEuZnJhbWUodG1wX1JUWyAsIDI6bGVuZ3RoKHRtcF9SVCldKQpyb3duYW1lcyhncl9SVF9zZCkgPC0gdG1wX1JUJGdyb3VwCgojIyBDb2VmZmljaWVudCBvZiB2YXJpYXRpb24KY3ZfUlQgPC0gMTAwICpncl9SVF9zZC9ncl9SVF9hdnIgCmN2X1JUIDwtIGRhdGEuZnJhbWUoZ3JvdXAgPSB0bXBfUlQkZ3JvdXAsIGN2X1JUKQpjdl9SVCRncm91cCA8LSBmYWN0b3IoY3ZfUlQkZ3JvdXAsIG9yZGVyZWQgPSBUUlVFLCAKICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlE3NDF4K2luaCIsICJXVCtpbmgiLCAiUTc0MXgiLCAiV1QiKSkKQ1ZfUlQgPC0gY3ZfUlQgJT4lIGdhdGhlcihQZXB0aWRlcywgQ1YsIC1ncm91cCkKCiMgQ2FsY3VsYXRlIG1lZGlhbi1DViBvZiBlYWNoIGdyb3VwCm1lZGlhbkNWX1JUIDwtIENWX1JUICU+JSBkcGx5cjo6Z3JvdXBfYnkoZ3JvdXApICU+JSBzdW1tYXJpc2UoQ1YgPSByb3VuZChtZWRpYW4oQ1YpLCAxKSkKYGBgCgpgYGB7cn0KcHJpbnQocGFzdGUwKCJNZWRpYW4tQ1Ygb2YgcGVwdGlkZSBSVDogUTc0MXgraW5oLCAiLCBtZWRpYW5DVl9SVFsxLDJdLCAiJTsgV1QraW5oLCAiLCBtZWRpYW5DVl9SVFsyLDJdLCAiJTsgUTc0MXgsICIsIG1lZGlhbkNWX1JUWzMsMl0sICIlOyBXVCwgIiwgbWVkaWFuQ1ZfUlRbNCwyXSwgIiUiKSkKYGBgCgpBbmQgaGVyZSBpcyB0aGUgcGxvdDsKYGBge3J9CiMgVmlvbGluIHBsb3Qgb2YgaW50ZXItZ3JvdXAgQ1YgCnBsb3QuY3ZfUlQgPC0gZ2dwbG90KENWX1JULCBhZXMoeD1ncm91cCwgeT1DVikpICsgCiAgICAgICAgICAgICAgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSBhcy5jaGFyYWN0ZXIoZ3JvdXApKSwgdHJpbT1GQUxTRSwgd2lkdGggPSAwLjgsICNhZXMoZmlsbCA9IGdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsIHBvc2l0aW9uID0gImRvZGdlIikrCiAgICAgICAgICAgICAgZ2VvbV9ib3hwbG90KHdpZHRoPTAuMSwgZmlsbCA9ICd3aGl0ZScsIG91dGxpZXIuc2l6ZSA9IDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSwgcG9zaXRpb24gPSAiZG9kZ2UiKSsKICAgICAgICAgICAgICAjZ2VvbV9ib3hwbG90KHdpZHRoPTAuMywgb3V0bGllci5zaXplID0gMC4xLCBuYS5ybSA9IFRSVUUsIHBvc2l0aW9uID0gImRvZGdlIiwgYWVzKGZpbGwgPSBhcy5jaGFyYWN0ZXIoZ3JvdXApKSkrCiAgICAgICAgICAgICAgZ2VvbV90ZXh0KGRhdGEgPSBtZWRpYW5DVl9SVCwgYWVzKGxhYmVsID0gQ1YpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gLTAuNSwgdmp1c3QgPSAtMC41LCBzaXplID0gNSkgKwogICAgICAgICAgICAgIHlsaW0oMCwgMjApKwogICAgICAgICAgICAgIGxhYnMoZmlsbCA9ICIiKSsKICAgICAgICAgICAgICB4bGFiKCIiKSArIHlsYWIoIiUgQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIG9mIHBlcHRpZGUgcmV0ZW50aW9uIHRpbWUiKSArCiAgICAgICAgICAgICAgdGhlbWVfbGlnaHQoYmFzZV9zaXplID0gMTIpCiAgICAgICAgICAgICAgCnBsb3QuY3ZfUlQKYGBgCgpBZGRpdGlvbmFsIGFuYWx5c2lzIzI6IFZpc3VhbGl6aW5nIHRoZSBvdmVyYWxsIHNoYXBlIG9mIGNvbXBhcmF0aXZlIGRhdGEgYnkgYSBoaXN0b2dyYW0gb2YgZGlzdHJpYnV0aW9uIG9mIGxvZzJGQzsKYGBge3J9Cmhpc3QobG9uZ19hbm8uZmMkbG9nMkZDLCBicmVha3MgPSAxMjAsIGNvbCA9ICJncmV5IiwgeGxhYiA9ICJsb2cyRkMiLCBtYWluID0gIiIpCmBgYAoKCg==